From: Fabian Gruenbichler Date: Wed, 16 Nov 2022 09:08:41 +0000 (+0100) Subject: Import rust-debcargo_2.6.0.orig.tar.gz X-Git-Tag: archive/raspbian/2.7.11-2+rpi1~1^2^2~2 X-Git-Url: https://dgit.raspbian.org/%22http:/www.example.com/%22mailto:kde%40ewsoftware.de//%22style.css/%22/%22http:/www.example.com/%22mailto:kde%40ewsoftware.de/%22style.css/%22?a=commitdiff_plain;h=1d68f1efa7527c0993c46a00e001b7c7dc8f6f9a;p=rust-debcargo.git Import rust-debcargo_2.6.0.orig.tar.gz [dgit import orig rust-debcargo_2.6.0.orig.tar.gz] --- 1d68f1efa7527c0993c46a00e001b7c7dc8f6f9a diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..a2ac07e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1720 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", +] + +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + +[[package]] +name = "bytesize" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c58ec36aac5066d5ca17df51b3e70279f5670a72102f5752cb7e7c856adfc70" + +[[package]] +name = "cargo" +version = "0.63.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d092a7c3e3aaa66469b2233b58c0bf330419dad9c423165f2b9cf1c57dc9f2e" +dependencies = [ + "anyhow", + "atty", + "bytesize", + "cargo-platform", + "cargo-util", + "clap", + "crates-io", + "crossbeam-utils", + "curl", + "curl-sys", + "env_logger", + "filetime", + "flate2", + "fwdansi", + "git2", + "git2-curl", + "glob", + "hex 0.4.3", + "home", + "humantime", + "ignore", + "im-rc", + "indexmap", + "itertools", + "jobserver", + "lazy_static", + "lazycell", + "libc", + "libgit2-sys", + "log", + "memchr", + "num_cpus", + "opener", + "os_info", + "pathdiff", + "percent-encoding", + "rustc-workspace-hack", + "rustfix", + "semver", + "serde", + "serde_ignored", + "serde_json", + "shell-escape", + "strip-ansi-escapes", + "tar", + "tempfile", + "termcolor", + "toml_edit", + "unicode-width", + "unicode-xid", + "url", + "walkdir", + "winapi", +] + +[[package]] +name = "cargo-platform" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-util" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75f6bfca7b85d6e8c6a42405e9b4ecadd2e63f75f94aabfb524378b57a557a4" +dependencies = [ + "anyhow", + "core-foundation", + "crypto-hash", + "filetime", + "hex 0.4.3", + "jobserver", + "libc", + "log", + "miow", + "same-file", + "shell-escape", + "tempfile", + "walkdir", + "winapi", +] + +[[package]] +name = "cc" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "clap" +version = "3.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "indexmap", + "once_cell", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "commoncrypto" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d056a8586ba25a1e4d61cb090900e495952c7886786fc55f909ab2f819b69007" +dependencies = [ + "commoncrypto-sys", +] + +[[package]] +name = "commoncrypto-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fed34f46747aa73dfaa578069fd8279d2818ade2b55f38f22a9401c7f4083e2" +dependencies = [ + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "crates-io" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4a87459133b2e708195eaab34be55039bc30e0d120658bd40794bb00b6328d" +dependencies = [ + "anyhow", + "curl", + "percent-encoding", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-hash" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a77162240fd97248d19a564a565eb563a3f592b386e4136fb300909e67dddca" +dependencies = [ + "commoncrypto", + "hex 0.3.2", + "openssl", + "winapi", +] + +[[package]] +name = "curl" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2", + "winapi", +] + +[[package]] +name = "curl-sys" +version = "0.4.59+curl-7.86.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cfce34829f448b08f55b7db6d0009e23e2e86a34e8c2b366269bf5799b4a407" +dependencies = [ + "cc", + "libc", + "libnghttp2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "winapi", +] + +[[package]] +name = "cxx" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "debcargo" +version = "2.6.0" +dependencies = [ + "ansi_term", + "anyhow", + "cargo", + "chrono", + "clap", + "env_logger", + "filetime", + "flate2", + "git2", + "glob", + "itertools", + "log", + "regex", + "semver", + "serde", + "serde_derive", + "tar", + "tempfile", + "textwrap", + "toml", + "walkdir", +] + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "filetime" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys 0.42.0", +] + +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "libz-sys", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fwdansi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c1f5787fe85505d1f7777268db5103d80a7a374d2316a7ce262e57baf8f208" +dependencies = [ + "memchr", + "termcolor", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "git2" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "git2-curl" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee51709364c341fbb6fe2a385a290fb9196753bdde2fc45447d27cd31b11b13" +dependencies = [ + "curl", + "git2", + "log", + "url", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "globset" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "home" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408" +dependencies = [ + "winapi", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +dependencies = [ + "crossbeam-utils", + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] +name = "im-rc" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe" +dependencies = [ + "bitmaps", + "rand_core", + "rand_xoshiro", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + +[[package]] +name = "jobserver" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kstring" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747" +dependencies = [ + "static_assertions", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "libgit2-sys" +version = "0.13.4+1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0fa6563431ede25f5cc7f6d803c6afbc1c5d3ad3d4925d12c882bf2b526f5d1" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libnghttp2-sys" +version = "0.1.7+1.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ed28aba195b38d5ff02b9170cbff627e336a20925e43b4945390401c5dc93f" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "libssh2-sys" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "miniz_oxide" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +dependencies = [ + "adler", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "opener" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea3ebcd72a54701f56345f16785a6d3ac2df7e986d273eb4395c0b01db17952" +dependencies = [ + "bstr", + "winapi", +] + +[[package]] +name = "openssl" +version = "0.10.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "os_info" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4750134fb6a5d49afc80777394ad5d95b04bc12068c6abb92fae8f43817270f" +dependencies = [ + "log", + "serde", + "winapi", +] + +[[package]] +name = "os_str_bytes" +version = "6.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e" + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rustc-workspace-hack" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc71d2faa173b74b232dedc235e3ee1696581bb132fc116fa3626d6151a1a8fb" + +[[package]] +name = "rustfix" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd2853d9e26988467753bd9912c3a126f642d05d229a4b53f5752ee36c56481" +dependencies = [ + "anyhow", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +dependencies = [ + "lazy_static", + "windows-sys 0.36.1", +] + +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + +[[package]] +name = "semver" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_ignored" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b3da7eedd967647a866f67829d1c79d184d7c4521126e9cc2c46a9585c6d21" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_json" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shell-escape" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" + +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "smawk" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strip-ansi-escapes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8" +dependencies = [ + "vte", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tar" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5376256e44f2443f8896ac012507c19a012df0fe8758b55246ae51a2279db51f" +dependencies = [ + "combine", + "indexmap", + "itertools", + "kstring", + "serde", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "unicode-linebreak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137" +dependencies = [ + "hashbrown", + "regex", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vte" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" +dependencies = [ + "arrayvec", + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ef08f9e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,92 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +name = "debcargo" +version = "2.6.0" +authors = [ + "Josh Triplett ", + "Ximin Luo ", + "Vasudev Kamath ", +] +description = "Create a Debian package from a Cargo crate." +readme = "README.md" +license = "MIT/Apache-2.0" +repository = "https://salsa.debian.org/rust-team/debcargo" +resolver = "2" + +[dependencies.ansi_term] +version = "0.12" + +[dependencies.anyhow] +version = "1.0" + +[dependencies.cargo] +version = "0.63" + +[dependencies.chrono] +version = "0.4" + +[dependencies.clap] +version = "3" +features = [ + "cargo", + "derive", +] + +[dependencies.env_logger] +version = "0.9" + +[dependencies.filetime] +version = "0.2" + +[dependencies.flate2] +version = "1" + +[dependencies.git2] +version = "0.14" + +[dependencies.glob] +version = "0.3" + +[dependencies.itertools] +version = "0.10" + +[dependencies.log] +version = "0.4" + +[dependencies.regex] +version = "1.0" + +[dependencies.semver] +version = "1" + +[dependencies.serde] +version = "1" + +[dependencies.serde_derive] +version = "1" + +[dependencies.tar] +version = "0.4" + +[dependencies.tempfile] +version = "3" + +[dependencies.textwrap] +version = "0.16" + +[dependencies.toml] +version = "0.5" + +[dependencies.walkdir] +version = "2" diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..67d3f48 --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,34 @@ +[package] +name = "debcargo" +version = "2.6.0" +authors = [ + "Josh Triplett ", + "Ximin Luo ", + "Vasudev Kamath "] +license = "MIT/Apache-2.0" +repository = "https://salsa.debian.org/rust-team/debcargo" +description = "Create a Debian package from a Cargo crate." +edition = "2021" + +[dependencies] +ansi_term = "0.12" +anyhow = "1.0" +cargo = "0.63" +clap = { version = "3", features = ["cargo", "derive"] } +chrono = "0.4" +env_logger = "0.9" +filetime = "0.2" +flate2 = "1" +git2 = "0.14" +glob = "0.3" +itertools = "0.10" +log = "0.4" +regex = "1.0" +semver = "1" +serde = "1" +serde_derive = "1" +tar = "0.4" +textwrap = "0.16" +tempfile = "3" +toml = "0.5" +walkdir = "2" diff --git a/HACKING.md b/HACKING.md new file mode 100644 index 0000000..ce4eb10 --- /dev/null +++ b/HACKING.md @@ -0,0 +1,87 @@ +This file contains information about developing debcargo itself. + + +## Dependencies + +For testing: + +```shell +# Install dependencies for building (see README.md), then: +$ cargo install cargo-tree # use https://github.com/infinity0/cargo-tree +$ apt-get install dh-cargo lintian +``` + +For development: + +```shell +# As above, then: +$ cargo install rustfmt cargo-graph cargo-outdated +$ cargo graph | dot -T png > graph.png +$ cargo outdated -R +``` + + +## Testing + +Whenever you make a major change, you should run: + + tests/sh/integrate.sh -kbr debcargo exa fd-find ripgrep + +in order to test it over a few hundred crates. Fix any build errors and +important lintian errors that crop up. + +If you make a change that has wide-reaching implications, such as messing with +the dependency logic, do a more thorough test: + + tests/sh/integrate.sh -kbR debcargo exa fd-find ripgrep mdbook sccache + +This will run the test over around a thousand crates. -R runs it over all the +transitive dependencies of all the binary packages, which is needed for entry +into Debian Testing. This is wider than -r, which runs the test over all the +transitive build-dependencies of the source package, which is needed for entry +into Debian Unstable. + +### Details + +To test the `debcargo` produced packages, you can run the following script. + +```shell +$ tests/sh/integrate.sh crate[s] +``` + +where you can provide a list of crate names or local directories containing +crates, and the script will run debcargo to create a source package (`.dsc`) +and run lintian over it. If you find any issues, please add to the bugs in +TODO.md file. + +```shell +$ tests/sh/integrate.sh -kb crate[s] +``` + +will additionally run [sbuild](https://wiki.debian.org/sbuild) on the source +package to build binary Debian packages (`.deb`) and run lintian over that too. +It will automatically pick up any extra .debs you already have in the output +directory, if they are dependencies of what you're building. The `-k` flag +tells the script not to wipe the directory before it does anything else. + +```shell +$ tests/sh/integrate.sh -kbr crate[s] +``` + +will run the script recursively over the listed crate(s) and all the transitive +build-dependencies of the generated source packages, in dependency order. This +covers all the dependencies that are needed for entry into Debian Unstable, and +typically covers a few hundred crates. You may want or need to edit or update +some of the overrides in `tests/configs`, to prune old or buggy dependencies. + +```shell +$ tests/sh/integrate.sh -kbR crate[s] +``` + +will run the script recursively over the listed crate(s) and all the transitive +runtime-dependencies of the binary packages, in dependency order. This covers +all the dependencies that are needed for entry into Debian Testing, and +typically covers a few thousand crates. You may want or need to edit or update +some of the overrides in `tests/configs`, to prune old or buggy dependencies. + +See `-h` for other options. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d996116 --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +Crates.io to Debian +=========================== + +`debcargo` creates Debian source package from the provided Rust crate. Create +Debian source package from the downloaded crate which follows Rust teams crate +packaging [policy](https://wiki.debian.org/Teams/RustPackaging/Policy). + + +## Features ## + + * Package specific versions of crates from crates.io. + * Easy to customize, using config files and overlay directories. + * Tries to auto-detect copyright information from metadata and actual + crate source, used to guess appropriate values for `debian/copyright`. + * Put `FIXME (hint)` strings where it can't detect full information, so user can + provide an override/overlay or manually fix it. + * Results in a lintian-clean Debian package in most cases. + * Packages can be cross-compiled by `sbuild` assuming the crate doesn't + include anything that breaks it (such as arch-specific build.rs scripts). + + +## Dependencies + +For building: + +```shell +$ apt-get build-dep cargo +$ apt-get install libssl-dev libcurl4-gnutls-dev quilt +$ cargo build debcargo +``` + + +## Examples ## + +To download and unpack the latest `clap` crate and prepare the source package: + +```shell +$ debcargo package clap +``` + +To download and unpack version `2.25.0` of `clap` crate and prepare the source package: + +```shell +$ debcargo package clap =2.25.0 +``` + +To provide additional packaging-specific config for downloading and packaging +latest `clap` crate from crates.io: + +```shell +$ debcargo package --config clap-2/debian/debcargo.toml clap +``` + +See `debcargo.toml.example` for a sample TOML file. + + +### Long-term maintenance workflow + +See https://salsa.debian.org/rust-team/debcargo-conf/blob/master/README.rst +for an example. + + +## License ## + +Debcargo is licensed under `MIT/Apache-2.0`. It is written by `Josh Triplett` +and improved by members of **Debian Rust Maintainers team** diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..d12394b --- /dev/null +++ b/TODO.md @@ -0,0 +1,28 @@ +See https://salsa.debian.org/groups/rust-team/-/issues + +See HACKING.md for development and testing instructions. + +## Lower-priority tasks + +Minor issues + +- fs::rename cannot handle cross-device moves, e.g. if --directory is on a + different partition from . then debcargo fails + +- the ? syntax loses the stack, use Result.expect() to give context, or use + error-chain instead... + +- [ ] globset, ignore, termcolor: + When generating d/copyright, failed to clone repository + https://github.com/BurntSushi/ripgrep/tree/master/XXX: unexpected HTTP status code: 404; class=Net (12) + +Features for later + +- [ ] Integrate `apt-pkg-native` crate to check if the crate or its dependency + is already in archive and display information. +- [ ] Display first level dependency with equivalent Debian names at the end + which are not yet packaged in Debian as a information to maintainer. +- [ ] A `dependency` sub-command to print all the dependencies including first + level and recursive using `cargo` API. +- [ ] Employ `licensecheck` tool to look for license and copyright information. + Currently we use regex to grep through sources. diff --git a/debcargo.toml.example b/debcargo.toml.example new file mode 100644 index 0000000..9fb0cfb --- /dev/null +++ b/debcargo.toml.example @@ -0,0 +1,241 @@ +# Whether to generate a package for the binary crate. If omitted, defaults to +# true unless semver_suffix (see below) is also true. +#bin = true + +# Name for the binary crate. Defaults to "" which means the crate +# name, with no "rust-" prefix and with underscores replaced by hyphens. +#bin_name = "" + +# Add the semver to the package name, to allow co-installation with other +# versions of the same crate. This should only be true for crates older than +# the most up-to-date version in Debian, and only if they are needed as a +# (direct or indirect) build dependency of another binary crate. +# +# If you set this to true, you should either omit "bin" or set it to false, +# unless you are sure the old and new packages are co-installable. +#semver_suffix = false + +# Overlay directory to copy on top of the generated one, given relative to the +# directory that contains this config file. If any files conflict with the ones +# generated by debcargo, the latter are moved to .debcargo.hint instead. +# +# For the special case of debian/changelog, generated entries will be prepended +# to the top of the existing d/changelog (from the overlay), rather than stored +# in debian/changelog.debcargo.hint. A further exception: if the distribution +# of the top entry in the existing d/changelog is +# UNRELEASED-FIXME-AUTOGENERATED-DEBCARGO then that whole entry will be +# *replaced* with the generated entry, not prepended. +# +# Normally, any "hints" generated are written back to the overlay directory, +# overwriting any hints that may have previously been stored there. For the +# special case of debian/changelog, that is written-back as-is. Therefore, it +# is strongly recommended that you keep the overlay directory tracked in +# version control. To disable this behaviour, give --no-overlay-write-back on +# the debcargo command line. +# +#overlay = "." + +# Local directory where crate can be found, instead of crates.io. Resolved +# relative to the directory that contains this config file. Note: this is +# currently experimental and only works for crates whose dependencies are all +# published on crates.io. For example, not rustup. This limitation will +# hopefully be fixed in the future. +# +#crate_src_path = "../.." + +# Paths from the crate tarball, to exclude from the orig tarball. +# See https://docs.rs/glob/latest/glob/struct.Pattern.html for syntax +#excludes = ["libgit2/**"] + +# Paths from the crate tarball, that have been manually reviewed to adhere to +# Debian policy. debcargo has a crude method for detecting files that might not +# fit within policy, and will give a fatal error if any are detected. In the +# exceptional cases where the method gives a false-positive, add them here. +#whitelist = ["libgit2/**"] + +# Whether to allow prerelease deps, by rewriting these to the released version. +# This should only be enabled for certain crates if really necessary, and first +# you should check that they can actually build when this is enabled. +#allow_prerelease_deps = false + +# This is the stem of the short description for each binary package. By default +# `debcargo` will try to auto-extract a description from `Cargo.toml` but +# sometimes this may lead to a meaningless, weird short description. +# +# The full short description of each binary package is constructed using this +# string plus an auto-generated suffix. The full string can be overridden by +# the [packages.KEY].summary config key, see below. +#summary = "PLACEHOLDER" + +# This is the stem of the long description for each binary package. By default +# this is empty. +# +# The full long description of each binary package is constructed using this +# string plus an auto-generated suffix. The full string can be overridden by +# the [packages.KEY].description config key, see below. +#description = """ +#PLACEHOLDER +#""" + +# Maintainer +# +# Defaults to pkg-rust-team, should only be overriden with care. If overridden, +# vcs_* in [source] should also be overridden, otherwise they point the Rust +# Team's salsa project. +# +#maintainer= "PLACEHOLDER" + +# Uploaders. This affects the Uploaders: field in debian/control as well as the +# additional maintainers listed in debian/copyright. The naming is historical; +# in Debian today for team-maintained packages, this is generally taken to mean +# "the main individuals" responsible for the package - anyone on the team is +# "morally allowed" to perform the upload as long as they specify "Team upload" +# in debian/changelog; this is done automatically by debcargo. +uploaders = [ "foo bar " ] + +# This is a temporary work-around in order to address situations where certain +# Debian infrastructure people claim (without supplying concrete evidence) that +# rust crate metadata is "too large". This flag addresses this, effectively by +# forcing all crate features together into a single feature. This increases the +# dependency footprint of the generated packages and therefore should not be +# enabled unless absolutely necessary. It can also cause cyclic dependencies in +# some cases, and in these cases it simply cannot be enabled, as packages in +# the cycle become uninstallable. Most crates should not need this, and you +# should not enable this just because "somebody told you so". +#collapse_features = false + +[source] + +# Debian Standards-Version to use. By default debcargo uses latest policy version. +#policy = "4.0.0" + +# Override or provide missing homepage for crate +#homepage = "https://clap.rs" + +# Override the VCS entries. +# By default this points to a relevant subdirectory underneath the main +# repository for debcargo config files, owned by the Debian Rust Maintainers: +# https://salsa.debian.org/rust-team/debcargo-conf/ +# Please only override this if your package is truly special, e.g. it combines +# lots of languages and/or there is not a crate at the top-level directory. In +# most cases you should prefer packaging as part of the Debian Rust Team, see +# https://wiki.debian.org/Teams/RustPackaging/Policy +# +#vcs_git = "https://salsa.debian.org/special_package/rust-special-0.1.git" +#vcs_browser = "https://salsa.debian.org/special_package/rust-special-0.1" + +# Section override for the source package. Unless overridden here, library +# crates get "rust" and non-library crates get a "FIXME". +#section = "rust" + +# Extra Build-Depends on top of those generated by debcargo. +# If you defined a custom d/rules that does extra stuff on top of dh-cargo, +# then you may need to use this. +# +# OTOH, if your crate needs external development headers to build, these should +# probably instead go in the [packages.lib] depends key rather than this key. +# debcargo will then automatically add those into the package Build-Depends if +# needed by dh-cargo; you don't have to add them here as well. +#build_depends = ["PLACEHOLDER", "PLACEHOLDER"] + +# Build-Depends to subtract from those generated by debcargo. This should be +# used when our default generated Build-Depends results in a cycle. For +# example, this might happen if: +# +# - crate A's default-feature depends on crate B's no-default-features (which has no dependencies) +# - crate B's default-feature depends on crate A's no-default-features (which has no dependencies) +# +# In these cases, you'll need to (for example) add "librustA+B-dev " +# to build_depends_excludes in A's debcargo.toml to break the cycle, and also +# add override_dh_auto_test to A's d/rules to avoid selecting the B feature +# when running the test build. Depending on the exact situation, it should be +# sufficient to do this override only for one of the packages in the cycle. +# +# Note that binary package Depends must be left alone in order to correctly +# express the dependencies; these ought not to have cycles in anyway, even in +# a case like the above example. +# +# This field should *not* be used to exclude arch-specific dependencies. We +# want to include them to support cross-compiling, and they should cause no +# problems since they are just source code. If our test "cargo build" fails for +# one of those dependencies, it should be handled in that package by disabling +# the failing test on the architectures that they are expected to fail on. +#build_depends_excludes = ["PLACEHOLDER", "PLACEHOLDER"] + +# Binary package overrides. +# +# Different values for KEY selects different binary packages: +# lib - the package for the library crate +# "lib+FEATURE" - the metapackage for feature FEATURE +# bin - the package for the binary crate +# +[packages.KEY] + +# Section override for the binary package. Use this if your crate is both a +# library and a binary crate; in this case, omit source.section which will +# default to "rust" and override this value for your binary package. +#section = "PLACEHOLDER" + +# Short description for the package. If omitted, debcargo autogenerates this +# using the top-level "summary" key plus a suffix describing the feature. +#summary = "PLACEHOLDER" + +# Long description for the package. If omitted, debcargo autogenerates this +# using the top-level "description" key plus a suffix describing the feature. +#description = """ +#PLACEHOLDER +#""" + +# Additional Depends on top of the ones generated by debcargo. This should be +# used to pull in system libraries for crates that need them to build. You'll +# want the -dev versions of the library packages, since our crate packages are +# development packages and not runtime packages. +#depends = ["PLACEHOLDER", "PLACEHOLDER"] + +# We generate an autopkgtest (post-install test) for every feature, and also +# run `cargo test` for the default feature set during build-time if there are +# no additional dev-dependencies. +# +# However sometimes this may not work, e.g. if the crate is part of a larger +# workspace and its tests require files from the workspace directory. Or if +# the crate author is simply negligent and didn't ensure the test passes for +# all features. In these cases, you can use this setting to mark the test as +# "flaky" to ignore failures. Special cases for packages.KEY: +# +# packages."lib+@" - disables the test for the --all-features autopkgtest +# +# The effect is transitive to its reverse-dependencies, so e.g. if you specify +# this for feature A, and feature B depends on feature A, then feature B also +# implicitly has this set. To unset it on feature B (and its transitive rdeps), +# explicit set this to false for feature B as well. +# +# So for example if the test breaks for the bare library (when running with +# --no-default-features) but works when the std feature is enabled, set this to +# true for [packages.lib] and false for [packages."lib+std"]. +# +# Note: debcargo will error if you set these in an inconsistent way. For +# example if feature A depends on {B, C}, B says true and C says false, of +# course we cannot determine what A should be. You can suppress the error by +# explicitly giving A a value, either true or false. +# +#test_is_broken = false + +# Some tests depend on extra system tools or libraries, which need to be given +# to autopkgtest. Like test_is_broken, the effect is transitive to its rdeps. +#test_depends = [] +# +# Note: we do not (currently) support unsetting of this in indirect rdeps. This +# would result in similar conflict issues as documented for test_is_broken. It +# is possible to work around it similarly, by requiring manual resolution - but +# cancelling dependencies is harder to implement than true/false so we avoid it +# for now. Please file an issue if you have a real use-case for it. + +# More additional fields. This is mostly useful for binary packages that might +# relate to other external programs, e.g. debcargo Recommends cargo. +#recommends = ["PLACEHOLDER", "PLACEHOLDER"] +#suggests = ["PLACEHOLDER", "PLACEHOLDER"] +#provides = ["PLACEHOLDER", "PLACEHOLDER"] + +# Extra lines to include in the stanza, freeform. Use this to include things +# that debcargo doesn't handle, such as Breaks, Conflicts, Replaces. +#extra_lines = ["PLACEHOLDER", "PLACEHOLDER"] diff --git a/src/bin/debcargo.rs b/src/bin/debcargo.rs new file mode 100644 index 0000000..039b3f3 --- /dev/null +++ b/src/bin/debcargo.rs @@ -0,0 +1,114 @@ +use ansi_term::Colour::Red; +use clap::{crate_version, AppSettings, Parser}; + +use debcargo::crates::CrateInfo; +use debcargo::debian::DebInfo; +use debcargo::errors::Result; +use debcargo::package::*; +use debcargo::{ + build_order::{build_order, BuildOrderArgs}, + crates::invalidate_crates_io_cache, +}; + +#[derive(Debug, Clone, Parser)] +#[clap(name = "debcargo", about = "Package Rust crates for Debian.")] +enum Opt { + /// Update the user's default crates.io index, outside of a workspace. + Update, + /// Print the Debian package name for a crate. + DebSrcName { + /// Name of the crate to package. + crate_name: String, + /// Version of the crate to package; may contain dependency operators. + /// If empty string, resolves to the latest version. If given here, + /// i.e. not omitted then print the package name as if the config + /// option semver_suffix was set to true. + version: Option, + }, + /// Extract only a crate, without any other transformations. + Extract { + #[clap(flatten)] + init: PackageInitArgs, + #[clap(flatten)] + extract: PackageExtractArgs, + }, + /// Package a Rust crate for Debian. + Package { + #[clap(flatten)] + init: PackageInitArgs, + #[clap(flatten)] + extract: PackageExtractArgs, + #[clap(flatten)] + finish: PackageExecuteArgs, + }, + /// Print the transitive dependencies of a package in topological order. + BuildOrder { + #[clap(flatten)] + args: BuildOrderArgs, + }, +} + +#[test] +fn verify_app() { + use clap::IntoApp; + Opt::into_app().debug_assert() +} + +fn real_main() -> Result<()> { + let m = Opt::clap() + .version(crate_version!()) + .global_setting(AppSettings::ColoredHelp) + .get_matches(); + use Opt::*; + match Opt::from_clap(&m) { + Update => invalidate_crates_io_cache(), + DebSrcName { + crate_name, + version, + } => { + let crate_info = CrateInfo::new_with_update(&crate_name, version.as_deref(), false)?; + let deb_info = DebInfo::new(&crate_info, crate_version!(), version.is_some()); + println!("{}", deb_info.package_name()); + Ok(()) + } + Extract { init, extract } => { + log::info!("preparing crate info"); + let mut process = PackageProcess::init(init)?; + log::info!("extracting crate"); + process.extract(extract)?; + Ok(()) + } + Package { + init, + extract, + finish, + } => { + log::info!("preparing crate info"); + let mut process = PackageProcess::init(init)?; + log::info!("extracting crate"); + process.extract(extract)?; + log::info!("applying overlay and patches"); + process.apply_overrides()?; + log::info!("preparing orig tarball"); + process.prepare_orig_tarball()?; + log::info!("preparing debian folder"); + process.prepare_debian_folder(finish)?; + process.post_package_checks() + } + BuildOrder { args } => { + let build_order = build_order(args)?; + for v in &build_order { + println!("{}", v); + } + Ok(()) + } + } +} + +fn main() { + env_logger::init(); + if let Err(e) = real_main() { + eprintln!("{}", Red.bold().paint(format!("debcargo failed: {:?}", e))); + std::process::exit(1); + } +} diff --git a/src/build_order.rs b/src/build_order.rs new file mode 100644 index 0000000..3425c94 --- /dev/null +++ b/src/build_order.rs @@ -0,0 +1,287 @@ +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque}; +use std::fmt; +use std::path::{Path, PathBuf}; + +use anyhow::Context; +use cargo::core::{Dependency, PackageId}; +use clap::{Parser, ValueEnum}; + +use crate::config::Config; +use crate::crates::{crate_name_ver_to_dep, show_dep, transitive_deps, CrateDepInfo, CrateInfo}; +use crate::debian::control::base_deb_name; +use crate::errors::Result; +use crate::package::{PackageExtractArgs, PackageProcess}; +use crate::util; + +#[derive(Debug, Clone, Copy, ValueEnum)] +#[clap(rename_all = "verbatim")] +pub enum ResolveType { + SourceForDebianUnstable, + BinaryAllForDebianTesting, +} + +#[derive(Debug, Clone, Parser)] +pub struct BuildOrderArgs { + /// Name of the crate to package. + crate_name: String, + /// Version of the crate to package; may contain dependency operators. + version: Option, + /// Directory for configs. The config subdirectory for a given crate is + /// looked up by their crate name and version, from more specific to less + /// specific, e.g. -1.2.3, then -1.2, then -1 and + /// finally . The config file is read from the debian/debcargo.toml + /// subpath of the looked-up subdirectory. + #[clap(long)] + config_dir: Option, + /// Resolution type + #[clap(value_enum, long, default_value = "SourceForDebianUnstable")] + resolve_type: ResolveType, + /// Emulate resolution as if every package were built with --collapse-features. + #[clap(long)] + emulate_collapse_features: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +struct PackageIdFeat(PackageId, &'static str); + +impl fmt::Display for PackageIdFeat { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}@{}/{}", self.0.name(), self.0.version(), self.1) + } +} + +// First result: if somebody build-depends on us, what do they first need to build? +// Second result: what other packages need to go into Debian Testing before us? +fn get_build_deps( + crate_details: &(CrateInfo, CrateDepInfo, Config), + package: &PackageIdFeat, + resolve_type: ResolveType, + emulate_collapse_features: bool, +) -> Result<(Vec, Vec)> { + let (_, crate_dep_info, config) = crate_details; + let all_deps = crate_dep_info + .iter() + .map(|(_, v)| v.1.iter()) + .flatten() + .cloned() + .collect::>(); + let feature_deps: HashSet = + HashSet::from_iter(transitive_deps(crate_dep_info, package.1).1); + let additional_deps = if emulate_collapse_features || config.collapse_features { + all_deps.clone() + } else { + // TODO: if build_depends_features is an override, use that instead of "default" + // TODO: also deprecate build_depends_excludes + HashSet::from_iter(transitive_deps(crate_dep_info, "default").1) + }; + let hard_deps = feature_deps + .union(&additional_deps) + .cloned() + .collect::>(); + use ResolveType::*; + match resolve_type { + SourceForDebianUnstable => Ok((hard_deps, vec![])), + BinaryAllForDebianTesting => { + let mut soft_deps = all_deps; + for h in hard_deps.iter() { + soft_deps.remove(h); + } + Ok((hard_deps, soft_deps.into_iter().collect::>())) + } + } +} + +fn dep_features(dep: &Dependency) -> Vec<&'static str> { + let mut feats = dep + .features() + .iter() + .map(|x| x.as_str()) + .collect::>(); + if dep.uses_default_features() { + feats.push("default") + } + feats.push(""); // bare-bones library with no features + feats +} + +fn find_config(config_dir: &Path, id: PackageId) -> Result<(Option, Config)> { + let name = base_deb_name(&id.name()); + let version = id.version(); + let candidates = [ + format!( + "{}-{}.{}.{}", + name, version.major, version.minor, version.patch + ), + format!("{}-{}.{}", name, version.major, version.minor), + format!("{}-{}", name, version.major), + name, + ]; + for c in candidates { + let path = config_dir.join(c).join("debian").join("debcargo.toml"); + if path.is_file() { + let config = Config::parse(&path).context("failed to parse debcargo.toml")?; + log::debug!("using config for {}: {:?}", id, path); + return Ok((Some(path), config)); + } + } + Ok((None, Config::default())) +} + +fn resolve_info( + infos: &mut BTreeMap, + cache: &mut HashMap, + config_dir: Option<&Path>, + dependency: &Dependency, + update: bool, +) -> Result { + if let Some(id) = cache.get(dependency) { + return Ok(*id); + } + + // resolve dependency + let info = CrateInfo::new_from_dependency(dependency, update)?; + let id = info.package_id(); + cache.insert(dependency.clone(), id); + + // insert info if it's not already there + if let std::collections::btree_map::Entry::Vacant(e) = infos.entry(id) { + let id = *e.key(); + let default_config = Config::default(); + let (config_path, config) = match config_dir { + None => (None, default_config), + Some(config_dir) => { + let (config_path, config) = find_config(config_dir, id)?; + match config_path { + None => (None, default_config), + Some(_) => (config_path, config), + } + } + }; + let (info, config) = match config_path { + None => (info, config), + Some(_) => { + let mut process = PackageProcess::new(info, config_path, config)?; + let tempdir = tempfile::Builder::new() + .prefix("debcargo") + .tempdir_in(".")?; + process.extract(PackageExtractArgs { + directory: Some(tempdir.path().to_path_buf()), + })?; + process.apply_overrides()?; + (process.crate_info, process.config) + } + }; + let dep_info = info.all_dependencies_and_features(); + e.insert((info, dep_info, config)); + }; + Ok(id) +} + +pub fn build_order(args: BuildOrderArgs) -> Result> { + let crate_name = &args.crate_name; + let version = args.version.as_deref(); + let config_dir = args.config_dir.as_deref(); + + let mut infos = BTreeMap::new(); + let mut cache = HashMap::new(); + let seed_dep = crate_name_ver_to_dep(crate_name, version)?; + let seed_id = resolve_info(&mut infos, &mut cache, config_dir, &seed_dep, true)?; + + let mut next = |idf: &PackageIdFeat| -> Result<(Vec, Vec)> { + let (hard, soft) = get_build_deps( + infos + .get(&idf.0) + .expect("build_order next called without crate info"), + idf, + args.resolve_type, + args.emulate_collapse_features, + )?; + log::trace!("{} hard-dep: {}", idf, util::show_vec_with(&hard, show_dep)); + if !soft.is_empty() { + log::trace!("{} soft-dep: {}", idf, util::show_vec_with(&soft, show_dep)); + } + // note: we might resolve the same crate-version several times; + // this is expected, since different dependencies (with different + // version ranges) might resolve into the same crate-version + let mut hard_p = Vec::new(); + for dep in hard { + let id = resolve_info(&mut infos, &mut cache, config_dir, &dep, false)?; + for f in dep_features(&dep) { + hard_p.push(PackageIdFeat(id, f)); + } + } + let mut soft_p = Vec::new(); + for dep in soft { + let id = resolve_info(&mut infos, &mut cache, config_dir, &dep, false)?; + for f in dep_features(&dep) { + soft_p.push(PackageIdFeat(id, f)); + } + } + log::trace!("{} hard-dep resolve: {}", idf, util::show_vec(&hard_p)); + if !soft_p.is_empty() { + log::trace!("{} soft-dep resolve: {}", idf, util::show_vec(&soft_p)); + } + Ok((hard_p, soft_p)) + }; + let mut i = 0; + let mut log = |remaining: &VecDeque<_>, graph: &BTreeMap<_, _>| { + i += 1; + if i % 16 == 0 { + debcargo_info!( + "debcargo build-order: resolving dependencies: done: {}, todo: {}", + graph.len(), + remaining.len() + ); + } + Ok(()) + }; + + let succ_with_features = + util::graph_from_succ([PackageIdFeat(seed_id, "")], &mut next, &mut log)?; + log::trace!("succ_with_features: {:#?}", succ_with_features); + + let succ = util::succ_proj(&succ_with_features, |x| x.0); + let pred = util::succ_to_pred(&succ); + let roots = succ + .iter() + .filter_map(|(k, v)| if v.is_empty() { Some(*k) } else { None }) + .collect::>(); + // swap pred/succ for call to topo_sort since we want reverse topo order + let build_order = match util::topo_sort(roots, pred.clone(), succ.clone()) { + Ok(r) => r, + Err(remain) => { + log::error!( + "topo_sort got cyclic graph: {:#?}", + remain + .into_iter() + .map(|(k, vv)| ( + k.to_string(), + vv.into_iter() + .map(|v| v.to_string()) + .collect::>() + )) + .collect::>() + ); + debcargo_bail!( + "topo_sort got cyclic graph; you'll need to patch the crate(s) to break the cycle." + ) + } + }; + + // sanity check + for p in build_order.iter() { + if infos.remove(p).is_none() { + log::error!("extra package in build-order not in infos: {}", p); + } + } + for (p, _) in infos { + log::error!( + "leftover infos not used in build-order: {}, succ: {}, pred: {}", + p, + util::show_vec(succ.get(&p).into_iter().flatten()), + util::show_vec(pred.get(&p).into_iter().flatten()), + ); + } + + Ok(build_order) +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..5f01f2a --- /dev/null +++ b/src/config.rs @@ -0,0 +1,259 @@ +use serde::Deserialize; +use toml; + +use crate::errors::*; + +use std::collections::HashMap; +use std::fs::File; +use std::io::Read; +use std::path::{Path, PathBuf}; + +pub const RUST_MAINT: &str = + "Debian Rust Maintainers "; + +#[derive(Deserialize, Debug, Clone)] +#[serde(default)] +pub struct Config { + pub bin: Option, + pub bin_name: String, + pub semver_suffix: bool, + pub overlay: Option, + pub excludes: Option>, + pub whitelist: Option>, + pub allow_prerelease_deps: bool, + pub crate_src_path: Option, + pub summary: Option, + pub description: Option, + pub maintainer: String, + pub uploaders: Option>, + pub collapse_features: bool, + pub requires_root: Option, + + pub source: Option, + pub packages: Option>, +} + +#[derive(Deserialize, Debug, Clone, Default)] +pub struct SourceOverride { + section: Option, + policy: Option, + homepage: Option, + vcs_git: Option, + vcs_browser: Option, + build_depends: Option>, + build_depends_excludes: Option>, +} + +#[derive(Deserialize, Debug, Clone, Default)] +pub struct PackageOverride { + section: Option, + summary: Option, + description: Option, + depends: Option>, + recommends: Option>, + suggests: Option>, + provides: Option>, + extra_lines: Option>, + test_is_broken: Option, + test_depends: Option>, +} + +impl Default for Config { + fn default() -> Self { + Config { + bin: None, + bin_name: "".to_string(), + semver_suffix: false, + overlay: None, + excludes: None, + whitelist: None, + allow_prerelease_deps: false, + crate_src_path: None, + summary: None, + description: None, + maintainer: RUST_MAINT.to_string(), + uploaders: None, + collapse_features: false, + source: None, + packages: None, + requires_root: None, + } + } +} + +impl Config { + pub fn parse(src: &Path) -> Result { + let mut config_file = File::open(src)?; + let mut content = String::new(); + config_file.read_to_string(&mut content)?; + + Ok(toml::from_str(&content)?) + } + + pub fn build_bin_package(&self) -> bool { + match self.bin { + None => !self.semver_suffix, + Some(b) => b, + } + } + + pub fn overlay_dir(&self, config_path: Option<&Path>) -> Option { + Some(config_path?.parent()?.join(self.overlay.as_ref()?)) + } + + pub fn crate_src_path(&self, config_path: Option<&Path>) -> Option { + Some(config_path?.parent()?.join(self.crate_src_path.as_ref()?)) + } + + pub fn orig_tar_excludes(&self) -> Option<&Vec> { + self.excludes.as_ref() + } + + pub fn orig_tar_whitelist(&self) -> Option<&Vec> { + self.whitelist.as_ref() + } + + pub fn maintainer(&self) -> &str { + self.maintainer.as_str() + } + + pub fn uploaders(&self) -> Option<&Vec> { + self.uploaders.as_ref() + } + + pub fn requires_root(&self) -> Option<&String> { + self.requires_root.as_ref() + } + + // Source shortcuts + + pub fn section(&self) -> Option<&str> { + Some(self.source.as_ref()?.section.as_ref()?) + } + + pub fn policy_version(&self) -> Option<&str> { + Some(self.source.as_ref()?.policy.as_ref()?) + } + + pub fn homepage(&self) -> Option<&str> { + Some(self.source.as_ref()?.homepage.as_ref()?) + } + + pub fn vcs_git(&self) -> Option<&str> { + Some(self.source.as_ref()?.vcs_git.as_ref()?) + } + + pub fn vcs_browser(&self) -> Option<&str> { + Some(self.source.as_ref()?.vcs_browser.as_ref()?) + } + + pub fn build_depends(&self) -> Option<&Vec> { + self.source.as_ref()?.build_depends.as_ref() + } + + pub fn build_depends_excludes(&self) -> Option<&Vec> { + self.source.as_ref()?.build_depends_excludes.as_ref() + } + + // Packages shortcuts + + fn with_package<'a, T, F: FnOnce(&'a PackageOverride) -> Option>( + &'a self, + key: PackageKey, + f: F, + ) -> Option { + self.packages + .as_ref()? + .get(&package_key_string(key)) + .and_then(f) + } + + pub fn package_section(&self, key: PackageKey) -> Option<&str> { + self.with_package(key, |pkg| pkg.section.as_deref()) + } + + pub fn package_summary(&self, key: PackageKey) -> Option<&str> { + self.with_package(key, |pkg| pkg.summary.as_deref()) + } + + pub fn package_description(&self, key: PackageKey) -> Option<&str> { + self.with_package(key, |pkg| pkg.description.as_deref()) + } + + pub fn package_depends(&self, key: PackageKey) -> Option<&Vec> { + self.with_package(key, |pkg| pkg.depends.as_ref()) + } + + pub fn package_recommends(&self, key: PackageKey) -> Option<&Vec> { + self.with_package(key, |pkg| pkg.recommends.as_ref()) + } + + pub fn package_suggests(&self, key: PackageKey) -> Option<&Vec> { + self.with_package(key, |pkg| pkg.suggests.as_ref()) + } + + pub fn package_provides(&self, key: PackageKey) -> Option<&Vec> { + self.with_package(key, |pkg| pkg.provides.as_ref()) + } + + pub fn package_extra_lines(&self, key: PackageKey) -> Option<&Vec> { + self.with_package(key, |pkg| pkg.extra_lines.as_ref()) + } + + pub fn package_test_is_broken(&self, key: PackageKey) -> Option { + self.with_package(key, |pkg| pkg.test_is_broken) + } + + pub fn package_test_depends(&self, key: PackageKey) -> Option<&Vec> { + self.with_package(key, |pkg| pkg.test_depends.as_ref()) + } +} + +pub fn package_field_for_feature<'a>( + get_field: &'a dyn Fn(PackageKey) -> Option<&'a Vec>, + feature: PackageKey, + f_provides: &[&str], +) -> Vec { + Some(feature) + .into_iter() + .chain(f_provides.iter().map(|s| PackageKey::feature(s))) + .map(move |f| get_field(f).into_iter().flatten()) + .flatten() + .map(|s| s.to_string()) + .collect() +} + +#[derive(Clone, Copy)] +pub enum PackageKey<'a> { + Bin, + BareLib, + FeatureLib(&'a str), +} + +impl<'a> PackageKey<'a> { + pub fn feature(f: &'a str) -> PackageKey<'a> { + use self::PackageKey::*; + if f.is_empty() { + BareLib + } else { + FeatureLib(f) + } + } +} + +fn package_key_string(key: PackageKey) -> String { + use self::PackageKey::*; + match key { + Bin => "bin".to_string(), + BareLib => "lib".to_string(), + FeatureLib(feature) => format!("lib+{}", feature), + } +} + +pub fn testing_ignore_debpolv() -> bool { + std::env::var("DEBCARGO_TESTING_IGNORE_DEBIAN_POLICY_VIOLATION") == Ok("1".to_string()) +} + +pub fn testing_ruzt() -> bool { + std::env::var("DEBCARGO_TESTING_RUZT") == Ok("1".to_string()) +} diff --git a/src/crates.rs b/src/crates.rs new file mode 100644 index 0000000..c57a61f --- /dev/null +++ b/src/crates.rs @@ -0,0 +1,717 @@ +use anyhow::{format_err, Error}; +use cargo::{ + core::manifest::ManifestMetadata, + core::registry::PackageRegistry, + core::source::MaybePackage, + core::{ + resolver::features::CliFeatures, Dependency, EitherManifest, FeatureValue, Manifest, + Package, PackageId, Registry, Source, SourceId, Summary, Target, TargetKind, Workspace, + }, + ops, + ops::{PackageOpts, Packages}, + sources::RegistrySource, + util::{interning::InternedString, toml::read_manifest, FileLock}, + Config, +}; +use filetime::{set_file_times, FileTime}; +use flate2::read::GzDecoder; +use glob::Pattern; +use regex::Regex; +use semver::Version; +use tar::Archive; +use tempfile; + +use std; +use std::collections::{BTreeMap, HashSet}; +use std::fs; +use std::hash::{Hash, Hasher}; +use std::io::{self, Read, Write}; +use std::path::Path; + +use crate::config::testing_ignore_debpolv; +use crate::errors::*; + +pub struct CrateInfo { + // only used for to_registry_toml in extract_crate. DO NOT USE ELSEWHERE + package: Package, + // allows overriding package.manifest() e.g. via patches + manifest: Manifest, + crate_file: FileLock, + config: Config, + source_id: SourceId, + excludes: Vec, + includes: Vec, +} + +pub type CrateDepInfo = BTreeMap< + &'static str, // name of feature / optional dependency, + // or "" for the base package w/ no default features, guaranteed to be in the map + ( + Vec<&'static str>, // dependencies: other features (of the current package) + Vec, + ), +>; + +fn hash(hashable: &H) -> u64 { + #![allow(deprecated)] + let mut hasher = std::hash::SipHasher::new(); + hashable.hash(&mut hasher); + hasher.finish() +} + +fn fetch_candidates(registry: &mut PackageRegistry, dep: &Dependency) -> Result> { + let mut summaries = match registry.query_vec(dep, false) { + std::task::Poll::Ready(res) => res?, + std::task::Poll::Pending => { + registry.block_until_ready()?; + return fetch_candidates(registry, dep); + } + }; + summaries.sort_by(|a, b| b.package_id().partial_cmp(&a.package_id()).unwrap()); + Ok(summaries) +} + +pub fn invalidate_crates_io_cache() -> Result<()> { + let config = Config::default()?; + let _lock = config.acquire_package_cache_lock()?; + let source_id = SourceId::crates_io(&config)?; + let yanked_whitelist = HashSet::new(); + let mut r = RegistrySource::remote(source_id, &yanked_whitelist, &config)?; + r.invalidate_cache(); + Ok(()) +} + +pub fn crate_name_ver_to_dep(crate_name: &str, version: Option<&str>) -> Result { + // note: this forces a network call + let config = Config::default()?; + let source_id = SourceId::crates_io(&config)?; + let version = version.and_then(|v| { + if v.is_empty() { + None + } else if v.starts_with(|c: char| c.is_digit(10)) { + Some(["=", v].concat()) + } else { + Some(v.to_string()) + } + }); + Dependency::parse(crate_name, version.as_deref(), source_id) +} + +pub fn show_dep(dep: &Dependency) -> String { + format!("{} {}", dep.package_name(), dep.version_req()) +} + +impl CrateInfo { + pub fn new(crate_name: &str, version: Option<&str>) -> Result { + CrateInfo::new_with_update(crate_name, version, true) + } + + pub fn new_with_local_crate( + crate_name: &str, + version: Option<&str>, + crate_path: &Path, + ) -> Result { + let config = Config::default()?; + let crate_path = crate_path.canonicalize()?; + let source_id = SourceId::for_path(&crate_path)?; + + let (package, crate_file) = { + let yanked_whitelist = HashSet::new(); + + let mut source = source_id.load(&config, &yanked_whitelist)?; + + let package_id = match version { + None | Some("") => { + let dep = Dependency::parse(crate_name, None, source_id)?; + let mut package_id: Option = None; + loop { + match source.query(&dep, &mut |p| package_id = Some(p.package_id())) { + std::task::Poll::Ready(res) => { + res?; + break; + } + std::task::Poll::Pending => { + source.block_until_ready()?; + } + } + } + package_id.unwrap() + } + Some(version) => PackageId::new(crate_name, version, source_id)?, + }; + + let maybe_package = source.download(package_id)?; + let package = match maybe_package { + MaybePackage::Ready(p) => Ok(p), + _ => Err(format_err!( + "Failed to 'download' local crate {} from {}", + crate_name, + crate_path.display() + )), + }?; + + let crate_file = { + let workspace = Workspace::ephemeral(package.clone(), &config, None, true)?; + + let opts = PackageOpts { + config: &config, + verify: false, + list: false, + check_metadata: true, + allow_dirty: true, + cli_features: CliFeatures::from_command_line(&[], true, false)?, + jobs: None, + targets: Vec::new(), + to_package: Packages::Default, + keep_going: false, + }; + + // as of cargo 0.41 this returns a FileLock with a temp path, instead of the one + // it got renamed to + if ops::package(&workspace, &opts)?.is_none() { + return Err(format_err!( + "Failed to assemble crate file for local crate {} at {}\n", + crate_name, + crate_path.display() + )); + } + let filename = format!("{}-{}.crate", crate_name, package_id.version().to_string()); + workspace + .target_dir() + .join("package") + .open_rw(&filename, &config, "crate file")? + }; + + (package, crate_file) + }; + + let manifest = package.manifest().clone(); + + Ok(CrateInfo { + package, + manifest, + crate_file, + config, + source_id, + excludes: vec![], + includes: vec![], + }) + } + + pub fn new_with_update( + crate_name: &str, + version: Option<&str>, + update: bool, + ) -> Result { + let dep = crate_name_ver_to_dep(crate_name, version)?; + Self::new_from_dependency(&dep, update) + } + + pub fn new_from_dependency(dependency: &Dependency, update: bool) -> Result { + let mut config = Config::default()?; + if !update { + // unfriendly API from cargo; we'll have to make do with it for + // now as there is no other alternative + config.configure( + 0, + false, + None, + config.frozen(), + config.locked(), + true, // offline + &config.target_dir()?.map(|x| x.into_path_unlocked()), + &[], + &[], + )?; + } + + let source_id = dependency.source_id(); + let registry_name = format!( + "{}-{:016x}", + source_id.url().host_str().unwrap_or(""), + hash(&source_id).swap_bytes() + ); + let get_package_info = |config: &Config| -> Result<_> { + let lock = config.acquire_package_cache_lock()?; + let mut registry = PackageRegistry::new(config)?; + registry.lock_patches(); + let summaries = fetch_candidates(&mut registry, dependency)?; + drop(lock); + let pkgids = summaries + .into_iter() + .map(|s| s.package_id()) + .collect::>(); + let pkgid = pkgids.iter().max().ok_or_else(|| { + format_err!( + concat!( + "Couldn't find any crate matching {}\n", + "Try `debcargo update` to update the crates.io index." + ), + show_dep(dependency) + ) + })?; + let pkgset = registry.get(pkgids.as_slice())?; + let package = pkgset.get_one(*pkgid)?; + let manifest = package.manifest(); + for f in dependency.features() { + // apparently, if offline is set then cargo sometimes selects + // an offline-available version that doesn't satisfy the + // requested features. this is dumb. if it happens, then we + // retry with online allowed. + if !manifest.summary().features().contains_key(f) { + debcargo_bail!( + "resolve ({} {}) -> ({}) failed to pick up required feature ({})\n\ + This can happen with very old or yanked crates. Try patching one of \ + its dependants, to drop or update the offending dependency.", + dependency.package_name(), + dependency.version_req(), + pkgid, + f, + ) + } + } + let filename = format!("{}-{}.crate", pkgid.name(), pkgid.version()); + let crate_file = config + .registry_cache_path() + .join(®istry_name) + .open_ro(&filename, config, &filename)?; + Ok((package.clone(), manifest.clone(), crate_file)) + }; + // if update is false but the user never downloaded the crate then the + // first call will error; re-try with online in that case + let (package, manifest, crate_file) = + get_package_info(&config).or_else(|_| get_package_info(&Config::default()?))?; + + Ok(CrateInfo { + package, + manifest, + crate_file, + config, + source_id, + excludes: vec![], + includes: vec![], + }) + } + + pub fn crate_name(&self) -> &'static str { + self.package_id().name().as_str() + } + + pub fn version(&self) -> &Version { + self.package_id().version() + } + + pub fn semver(&self) -> String { + match *self.package_id().version() { + Version { + major: 0, minor, .. + } => format!("0.{}", minor), + Version { major, .. } => format!("{}", major), + } + } + + pub fn manifest(&self) -> &Manifest { + &self.manifest + } + + pub fn replace_manifest(&mut self, path: &Path) -> Result<&Self> { + if let (EitherManifest::Real(v), _) = read_manifest(path, self.source_id, &self.config)? { + self.manifest = v; + } + Ok(self) + } + + pub fn metadata(&self) -> &ManifestMetadata { + self.manifest.metadata() + } + + pub fn manifest_path(&self) -> &Path { + self.package.manifest_path() + } + + pub fn targets(&self) -> &[Target] { + self.manifest.targets() + } + + pub fn is_lib(&self) -> bool { + let mut lib = false; + for target in self.manifest.targets() { + match *target.kind() { + TargetKind::Lib(_) => { + lib = true; + break; + } + _ => continue, + } + } + lib + } + + pub fn get_binary_targets(&self) -> Vec<&str> { + let mut bins = Vec::new(); + for target in self.manifest.targets() { + match *target.kind() { + TargetKind::Bin => { + bins.push(target.name()); + } + _ => continue, + } + } + bins.sort_unstable(); + bins + } + + pub fn summary(&self) -> &Summary { + self.manifest.summary() + } + + pub fn checksum(&self) -> Option<&str> { + self.manifest.summary().checksum() + } + + pub fn package_id(&self) -> PackageId { + self.manifest.summary().package_id() + } + + pub fn crate_file(&self) -> &FileLock { + &self.crate_file + } + + pub fn dependencies(&self) -> &[Dependency] { + self.manifest.dependencies() + } + + pub fn dev_dependencies(&self) -> Vec { + use cargo::core::dependency::DepKind; + let mut deps = vec![]; + for dep in self.dependencies() { + if dep.kind() == DepKind::Development { + deps.push(dep.clone()) + } + } + deps + } + + /// Collect information about the dependency structure of features and + /// their external crate dependencies, in a simple output format. + pub fn all_dependencies_and_features(&self) -> CrateDepInfo { + use cargo::core::dependency::DepKind; + + let mut deps_by_name: BTreeMap<&str, Vec<&Dependency>> = BTreeMap::new(); + for dep in self.dependencies() { + // we treat build-dependencies also as dependencies in Debian + if dep.kind() != DepKind::Development { + let s = dep.name_in_toml().as_str(); + deps_by_name.entry(s).or_default().push(dep); + } + } + let deps_by_name = deps_by_name; + + let mut features_with_deps = BTreeMap::new(); + + // calculate dependencies of this crate's features + for (feature, deps) in self.manifest.summary().features() { + let mut feature_deps: Vec<&'static str> = vec![]; + let mut other_deps: Vec = Vec::new(); + for dep in deps { + use self::FeatureValue::*; + match dep { + // another feature is a dependency + Feature(dep_feature) => { + feature_deps.push(InternedString::new(dep_feature).as_str()) + } + // another package is a dependency + Dep { dep_name } => { + // unwrap is ok, valid Cargo.toml files must have this + for &dep in deps_by_name.get(dep_name.as_str()).unwrap() { + other_deps.push(dep.clone()); + } + } + // another package is a dependency + DepFeature { + dep_name, + dep_feature, + .. + } => match deps_by_name.get(dep_name.as_str()) { + // unwrap is ok, valid Cargo.toml files must have this + Some(dd) => { + for &dep in dd { + let mut dep = dep.clone(); + let mut features: Vec = + vec![InternedString::new(dep_feature)]; + features.extend(dep.features()); + dep.set_features(features); + dep.set_default_features(false); + other_deps.push(dep); + } + } + None => { + let mut expected = false; + for dep in self.dependencies() { + if dep.kind() == DepKind::Development { + let s = dep.name_in_toml().as_str(); + if s == dep_name.as_str() { + expected = true; + } + } + } + if expected { + debcargo_warn!( + "Ignoring \"{}\" feature \"{}\" as it depends on a \ + dev-dependency \"{}\"", + self.package_id(), + feature, + dep_name + ); + } else { + panic!( + "failed to account for dependency \"{}\" of \"{}\" feature \"{}\"", + dep_name, self.package_id(), feature + ); + } + } + }, + } + } + if feature_deps.is_empty() { + // everything depends on bare library + feature_deps.push(""); + } + features_with_deps.insert(feature.as_str(), (feature_deps, other_deps)); + } + + // calculate required dependencies for implicit no-default-features + let mut deps_required: Vec = Vec::new(); + for deps in deps_by_name.values() { + for &dep in deps { + if !dep.is_optional() { + deps_required.push(dep.clone()) + } + } + } + + // implicit no-default-features + features_with_deps.insert("", (vec![], deps_required)); + + // implicit default feature + if !features_with_deps.contains_key("default") { + features_with_deps.insert("default", (vec![""], vec![])); + } + + features_with_deps + } + + pub fn get_summary_description(&self) -> (Option, Option) { + let (summary, description) = if let Some(ref description) = self.metadata().description { + // Convention these days seems to be to do manual text + // wrapping in crate descriptions, boo. \n\n is a real line break. + let mut description = description + .replace("\n\n", "\r") + .replace("\n", " ") + .replace("\r", "\n") + .trim() + .to_string(); + // Trim off common prefixes + let re = Regex::new(&format!( + r"^(?i)({}|This(\s+\w+)?)(\s*,|\s+is|\s+provides)\s+", + self.package_id().name() + )) + .unwrap(); + description = re.replace(&description, "").to_string(); + let re = Regex::new(r"^(?i)(a|an|the)\s+").unwrap(); + description = re.replace(&description, "").to_string(); + let re = + Regex::new(r"^(?i)(rust\s+)?(implementation|library|tool|crate)\s+(of|to|for)\s+") + .unwrap(); + description = re.replace(&description, "").to_string(); + + // https://stackoverflow.com/questions/38406793/why-is-capitalizing-the-first-letter-of-a-string-so-convoluted-in-rust + description = { + let mut d = description.chars(); + match d.next() { + None => String::new(), + Some(f) => f.to_uppercase().chain(d).collect::(), + } + }; + + // Use the first sentence or first line, whichever comes first, as the summary. + let p1 = description.find('\n'); + let p2 = description.find(". "); + match p1.into_iter().chain(p2.into_iter()).min() { + Some(p) => { + let s = description[..p].trim_end_matches('.').to_string(); + let d = description[p + 1..].trim(); + if d.is_empty() { + (Some(s), None) + } else { + (Some(s), Some(d.to_string())) + } + } + None => (Some(description.trim_end_matches('.').to_string()), None), + } + } else { + (None, None) + }; + + (summary, description) + } + + /// To be called before extract_crate. + pub fn set_includes_excludes( + &mut self, + excludes: Option<&Vec>, + includes: Option<&Vec>, + ) { + self.excludes = excludes + .into_iter() + .flatten() + .map(|x| Pattern::new(&("*/".to_owned() + x)).unwrap()) + .collect::>(); + self.includes = includes + .into_iter() + .flatten() + .map(|x| Pattern::new(&("*/".to_owned() + x)).unwrap()) + .collect::>(); + } + + pub fn filter_path(&self, path: &Path) -> ::std::result::Result { + if self.excludes.iter().any(|p| p.matches_path(path)) { + return Ok(true); + } + let suspicious = match path.extension() { + Some(ext) => ext == "c" || ext == "a", + _ => false, + }; + if suspicious { + if self.includes.iter().any(|p| p.matches_path(path)) { + debcargo_info!("Suspicious file, on whitelist so ignored: {:?}", path); + Ok(false) + } else if testing_ignore_debpolv() { + debcargo_warn!("Suspicious file, ignoring as per override: {:?}", path); + Ok(false) + } else { + Err(format!( + "Suspicious file, should probably be excluded: {:?}", + path + )) + } + } else { + Ok(false) + } + } + + pub fn extract_crate(&self, path: &Path) -> Result { + let mut archive = Archive::new(GzDecoder::new(self.crate_file.file())); + let tempdir = tempfile::Builder::new() + .prefix("debcargo") + .tempdir_in(".")?; + let mut source_modified = false; + let mut last_mtime = 0; + let mut err = vec![]; + + for entry in archive.entries()? { + let mut entry = entry?; + match self.filter_path(&(entry.path()?)) { + Err(e) => err.push(e), + Ok(r) => { + if r { + source_modified = true; + continue; + } + } + } + + if !entry.unpack_in(tempdir.path())? { + debcargo_bail!("Crate contained path traversals via '..'"); + } + + if let Ok(mtime) = entry.header().mtime() { + if mtime > last_mtime { + last_mtime = mtime; + } + } + } + if !err.is_empty() { + for e in err { + debcargo_warn!("{}", e); + } + debcargo_bail!( + "Suspicious files detected, aborting. Ask on #debian-rust if you are stuck." + ) + } + + let entries = tempdir.path().read_dir()?.collect::>>()?; + if entries.len() != 1 || !entries[0].file_type()?.is_dir() { + let pkgid = self.package_id(); + debcargo_bail!( + "{}-{}.crate did not unpack to a single top-level directory", + pkgid.name(), + pkgid.version() + ); + } + + if let Err(e) = fs::rename(entries[0].path(), &path) { + return Err(Error::from(e).context(format!( + concat!( + "Could not create source directory {0}\n", + "To regenerate, move or remove {0}" + ), + path.display() + ))); + } + + // Ensure that Cargo.toml is in standard form, e.g. does not contain + // path dependencies, so can be built standalone (see #4030). + let toml_path = path.join("Cargo.toml"); + let ws = Workspace::new(&toml_path.canonicalize()?, &self.config)?; + let registry_toml = self.package.to_registry_toml(&ws)?; + let mut actual_toml = String::new(); + fs::File::open(&toml_path)?.read_to_string(&mut actual_toml)?; + + if !actual_toml.contains("AUTOMATICALLY GENERATED BY CARGO") { + // This logic should only fire for old crates, and that's what the + // if-conditional is supposed to check; modern versions of cargo + // already do this before uploading the crate and we shouldn't need + // to handle it specially. + let old_toml_path = path.join("Cargo.toml.orig"); + fs::copy(&toml_path, &old_toml_path)?; + fs::OpenOptions::new() + .write(true) + .truncate(true) + .open(&toml_path)? + .write_all(registry_toml.as_bytes())?; + debcargo_info!( + "Rewrote {:?} to canonical form\nOld backed up as {:?}", + &toml_path, + &old_toml_path, + ); + source_modified = true; + // avoid lintian errors about package-contains-ancient-file + // TODO: do we want to do this for unmodified tarballs? it would + // force us to modify them, but otherwise we get that ugly warning + let last_mtime = FileTime::from_unix_time(last_mtime as i64, 0); + set_file_times(toml_path, last_mtime, last_mtime)?; + } + Ok(source_modified) + } +} + +/// Calculate all feature-dependencies and external-dependencies of a given +/// feature, using the information previously generated by +/// `all_dependencies_and_features`. +pub fn transitive_deps<'a>( + features_with_deps: &'a CrateDepInfo, + feature: &str, +) -> (Vec<&'a str>, Vec) { + let mut all_features = Vec::new(); + let mut all_deps = Vec::new(); + let &(ref ff, ref dd) = features_with_deps.get(feature).unwrap(); + all_features.extend(ff.clone()); + all_deps.extend(dd.clone()); + for f in ff { + let (ff1, dd1) = transitive_deps(features_with_deps, f); + all_features.extend(ff1); + all_deps.extend(dd1); + } + (all_features, all_deps) +} diff --git a/src/debian/changelog.rs b/src/debian/changelog.rs new file mode 100644 index 0000000..053006a --- /dev/null +++ b/src/debian/changelog.rs @@ -0,0 +1,183 @@ +use anyhow; +use chrono::{DateTime, FixedOffset, Local, TimeZone}; +use regex::Regex; + +use std::fmt; +use std::str; + +pub const DEFAULT_DIST: &str = "UNRELEASED-FIXME-AUTOGENERATED-DEBCARGO"; +pub const COMMENT_TEAM_UPLOAD: &str = " * Team upload."; + +pub struct ChangelogEntry { + pub source: String, + pub version: String, + pub distribution: String, + pub options: String, + pub maintainer: String, + pub date: DateTime, + pub items: Vec, +} + +pub fn local_now() -> DateTime { + let now = Local::now(); + let offset = now + .timezone() + .offset_from_local_datetime(&now.naive_local()) + .unwrap(); + now.with_timezone(&offset) +} + +impl fmt::Display for ChangelogEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!( + f, + "{} ({}) {}; {}\n", + self.source, self.version, self.distribution, self.options + )?; + + for entry in self.items.iter() { + writeln!(f, "{}", entry)?; + } + + writeln!(f, "\n -- {} {}", self.maintainer, self.date.to_rfc2822()) + } +} + +fn line_is_blank(s: &str) -> bool { + s.chars().all(char::is_whitespace) +} + +impl str::FromStr for ChangelogEntry { + type Err = anyhow::Error; + fn from_str(s: &str) -> Result { + let mut lines = s.lines().collect::>(); + // see https://manpages.debian.org/testing/dpkg-dev/deb-changelog.5.en.html + // regexes adapted from /usr/share/perl5/Dpkg/Changelog/Entry/Debian.pm + + let firstline = lines[0]; + let re1 = + Regex::new(r"(?i)^(\w[-+0-9a-z.]*) \(([^\(\) \t]+)\)((?:\s+[-+0-9a-z.]+)+);(.*?)\s*$") + .unwrap(); + let matches1 = re1.captures(firstline).unwrap(); + let mut i = 1; + while line_is_blank(lines[i]) { + i += 1; + } + lines = lines.split_off(i); + + while line_is_blank(lines.last().unwrap()) { + lines.pop(); + } + let lastline = lines.pop().unwrap(); + while line_is_blank(lines.last().unwrap()) { + lines.pop(); + } + let re2 = Regex::new(r"^ \-\- ((?:.*) <(?:.*)>) ?(\w.*\S)\s*$").unwrap(); + let matches2 = re2.captures(lastline).unwrap(); + + Ok(Self::new( + matches1[1].to_string(), + matches1[2].to_string(), + matches1[3].to_string(), + matches1[4].to_string(), + matches2[1].to_string(), + DateTime::parse_from_rfc2822(&matches2[2])?, + lines.iter().map(|s| s.to_string()).collect(), + )) + } +} + +impl ChangelogEntry { + pub fn new( + source: String, + version: String, + distribution: String, + options: String, + maintainer: String, + date: DateTime, + items: Vec, + ) -> Self { + ChangelogEntry { + source, + version, + distribution, + options, + maintainer, + date, + items, + } + } + + pub fn maintainer_name(self: &ChangelogEntry) -> String { + let re = Regex::new(r"^\s*(\S.*\S)\s*<.*>\s*$").unwrap(); + let matches = re.captures(&self.maintainer).unwrap(); + matches[1].to_string() + } + + pub fn version_parts(self: &ChangelogEntry) -> (String, String) { + let re = Regex::new(r"^(.*)-([^-]*)$").unwrap(); + let matches = re.captures(&self.version).unwrap(); + (matches[1].to_string(), matches[2].to_string()) + } + + pub fn deb_version_suffix(self: &ChangelogEntry) -> String { + let re = Regex::new(r".*-([^-]*)$").unwrap(); + re.captures(&self.version).unwrap()[1].to_string() + } + + pub fn deb_version_suffix_bump(self: &ChangelogEntry) -> String { + let suf = self.deb_version_suffix(); + let re = Regex::new(r"^((?:.*\D)?)(\d*)$").unwrap(); + let matches = re.captures(&suf).unwrap(); + if matches[2].is_empty() { + format!("{}.1", &matches[1]) + } else { + format!( + "{}{}", + &matches[1], + (matches[2].parse::().unwrap() + 1) + ) + } + } +} + +pub struct ChangelogIterator<'a> { + input: &'a [u8], + index: usize, +} + +impl<'a> ChangelogIterator<'a> { + pub fn from(input: &'a str) -> ChangelogIterator<'a> { + ChangelogIterator { + input: input.as_bytes(), + index: 0, + } + } +} + +impl<'a> Iterator for ChangelogIterator<'a> { + type Item = &'a str; + + #[inline] + fn next(&mut self) -> Option<&'a str> { + let slice = &self.input[self.index..]; + if slice.is_empty() { + return None; + } + let mut result = slice; + // ghetto parser; also hack around the fact rust's str doesn't + // support proper indexing, boo + for (i, c) in slice.iter().enumerate() { + if *c != b'\n' { + continue; + } + if i < (slice.len() - 1) && (slice[i + 1] as char).is_whitespace() { + continue; + } + self.index += i + 1; + result = &slice[..=i]; + break; + } + Some(str::from_utf8(result).unwrap()) + } +} diff --git a/src/debian/control.rs b/src/debian/control.rs new file mode 100644 index 0000000..3a77e13 --- /dev/null +++ b/src/debian/control.rs @@ -0,0 +1,590 @@ +use std::env::{self, VarError}; +use std::fmt::{self, Write}; + +use anyhow::{format_err, Error}; +use semver::Version; +use textwrap::fill; + +use crate::config::{self, Config, PackageKey}; +use crate::errors::*; + +pub struct Source { + name: String, + section: String, + priority: String, + maintainer: String, + uploaders: Vec, + standards: String, + build_deps: Vec, + vcs_git: String, + vcs_browser: String, + homepage: String, + crate_name: String, + requires_root: String, +} + +pub struct Package { + name: String, + arch: String, + multi_arch: String, + section: Option, + depends: Vec, + recommends: Vec, + suggests: Vec, + provides: Vec, + summary: Description, + description: Description, + extra_lines: Vec, +} + +pub struct Description { + pub prefix: String, + pub suffix: String, +} + +pub struct PkgTest { + name: String, + crate_name: String, + feature: String, + version: String, + extra_test_args: Vec, + depends: Vec, + extra_restricts: Vec, +} + +impl fmt::Display for Source { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "Source: {}", self.name)?; + writeln!(f, "Section: {}", self.section)?; + writeln!(f, "Priority: {}", self.priority)?; + writeln!(f, "Build-Depends: {}", self.build_deps.join(",\n "))?; + writeln!(f, "Maintainer: {}", self.maintainer)?; + if !self.uploaders.is_empty() { + writeln!(f, "Uploaders:\n {}", self.uploaders.join(",\n "))?; + } + writeln!(f, "Standards-Version: {}", self.standards)?; + writeln!(f, "Vcs-Git: {}", self.vcs_git)?; + writeln!(f, "Vcs-Browser: {}", self.vcs_browser)?; + + if !self.homepage.is_empty() { + writeln!(f, "Homepage: {}", self.homepage)?; + } + + // We used to set this conditionally, however it's best to do it + // unconditionally as some crates' names have a number suffix e.g. + // "utf-8". Without setting X-Cargo-Crate, we cannot distinguish: + // a) "utf" crate at version 8 with semver_suffix = true + // b) "utf-8" crate at version latest with semver_suffix = false. + // dh-cargo assumes (a) which is wrong for the "utf-8" crate + writeln!(f, "X-Cargo-Crate: {}", self.crate_name)?; + writeln!(f, "Rules-Requires-Root: {}", self.requires_root)?; + + Ok(()) + } +} + +impl fmt::Display for Package { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "Package: {}", self.name)?; + writeln!(f, "Architecture: {}", self.arch)?; + writeln!(f, "Multi-Arch: {}", self.multi_arch)?; + if let Some(section) = &self.section { + writeln!(f, "Section: {}", section)?; + } + + if !self.depends.is_empty() { + writeln!(f, "Depends:\n {}", self.depends.join(",\n "))?; + } + if !self.recommends.is_empty() { + writeln!(f, "Recommends:\n {}", self.recommends.join(",\n "))?; + } + if !self.suggests.is_empty() { + writeln!(f, "Suggests:\n {}", self.suggests.join(",\n "))?; + } + if !self.provides.is_empty() { + writeln!(f, "Provides:\n {}", self.provides.join(",\n "))?; + } + + for line in &self.extra_lines { + writeln!(f, "{}", line)?; + } + + self.write_description(f) + } +} + +impl fmt::Display for PkgTest { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let extra_args = if self.extra_test_args.is_empty() { + "".into() + } else { + format!(" {}", self.extra_test_args.join(" ")) + }; + writeln!( + f, + "Test-Command: /usr/share/cargo/bin/cargo-auto-test {} {} --all-targets{}", + self.crate_name, self.version, extra_args, + )?; + writeln!(f, "Features: test-name={}:{}", &self.name, &self.feature)?; + // TODO: drop the below workaround when rust-lang/cargo#5133 is fixed. + // The downside of our present work-around is that more dependencies + // must be installed, which makes it harder to actually run the tests + let cargo_bug_fixed = false; + let default_deps = if cargo_bug_fixed { &self.name } else { "@" }; + + let depends = if self.depends.is_empty() { + "".into() + } else { + format!(", {}", self.depends.join(", ")) + }; + writeln!(f, "Depends: dh-cargo (>= 18){}, {}", depends, default_deps)?; + + let restricts = if self.extra_restricts.is_empty() { + "".into() + } else { + format!(", {}", self.extra_restricts.join(", ")) + }; + writeln!( + f, + "Restrictions: allow-stderr, skip-not-installable{}", + restricts, + )?; + Ok(()) + } +} + +impl Source { + pub fn pkg_prefix() -> &'static str { + if config::testing_ruzt() { + // avoid accidentally installing official packages during tests + "ruzt" + } else { + "rust" + } + } + + #[allow(clippy::too_many_arguments)] + pub fn new( + basename: &str, + name_suffix: Option<&str>, + crate_name: &str, + home: &str, + lib: bool, + maintainer: String, + uploaders: Vec, + build_deps: Vec, + _requires_root: String, + ) -> Result { + let pkgbase = match name_suffix { + None => basename.to_string(), + Some(suf) => format!("{}{}", basename, suf), + }; + let section = if lib { + "rust" + } else { + "FIXME-IN-THE-SOURCE-SECTION" + }; + let priority = "optional".to_string(); + let vcs_browser = format!( + "https://salsa.debian.org/rust-team/debcargo-conf/tree/master/src/{}", + pkgbase + ); + let vcs_git = format!( + "https://salsa.debian.org/rust-team/debcargo-conf.git [src/{}]", + pkgbase + ); + Ok(Source { + name: dsc_name(&pkgbase), + section: section.to_string(), + priority, + maintainer, + uploaders, + standards: "4.6.1".to_string(), + build_deps, + vcs_git, + vcs_browser, + homepage: home.to_string(), + crate_name: crate_name.to_string(), + requires_root: "no".to_string(), + }) + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn apply_overrides(&mut self, config: &Config) { + if let Some(section) = config.section() { + self.section = section.to_string(); + } + + if let Some(policy) = config.policy_version() { + self.standards = policy.to_string(); + } + + self.build_deps.extend( + config + .build_depends() + .into_iter() + .flatten() + .map(String::to_string), + ); + let bdeps_ex = config + .build_depends_excludes() + .map(Vec::as_slice) + .unwrap_or(&[]); + self.build_deps.retain(|x| !bdeps_ex.contains(x)); + + if let Some(homepage) = config.homepage() { + self.homepage = homepage.to_string(); + } + + if let Some(vcs_git) = config.vcs_git() { + self.vcs_git = vcs_git.to_string(); + } + + if let Some(vcs_browser) = config.vcs_browser() { + self.vcs_browser = vcs_browser.to_string(); + } + + if let Some(requires_root) = config.requires_root() { + self.requires_root = requires_root.to_string(); + } + } +} + +impl Package { + pub fn pkg_prefix() -> &'static str { + if config::testing_ruzt() { + // avoid accidentally installing official packages during tests + "libruzt" + } else { + "librust" + } + } + + #[allow(clippy::too_many_arguments)] + pub fn new( + basename: &str, + name_suffix: Option<&str>, + version: &Version, + summary: Description, + description: Description, + feature: Option<&str>, + f_deps: Vec<&str>, + o_deps: Vec, + f_provides: Vec<&str>, + f_recommends: Vec<&str>, + f_suggests: Vec<&str>, + ) -> Result { + let pkgbase = match name_suffix { + None => basename.to_string(), + Some(suf) => format!("{}{}", basename, suf), + }; + let deb_feature2 = &|p: &str, f: &str| { + format!( + "{} (= ${{binary:Version}})", + match f { + "" => deb_name(p), + _ => deb_feature_name(p, f), + } + ) + }; + let deb_feature = &|f: &str| deb_feature2(&pkgbase, f); + + let filter_provides = &|x: Vec<&str>| { + x.into_iter() + .filter(|f| !f_provides.contains(f)) + .map(deb_feature) + .collect() + }; + let (recommends, suggests) = match feature { + Some(_) => (vec![], vec![]), + None => (filter_provides(f_recommends), filter_provides(f_suggests)), + }; + + // Provides for all possible versions, see: + // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=901827#35 + // https://wiki.debian.org/Teams/RustPackaging/Policy#Package_provides + let mut provides = vec![]; + let version_suffixes = vec![ + "".to_string(), + format!("-{}", version.major), + format!("-{}.{}", version.major, version.minor), + format!("-{}.{}.{}", version.major, version.minor, version.patch), + ]; + for suffix in version_suffixes.iter() { + let p = format!("{}{}", basename, suffix); + provides.push(deb_feature2(&p, feature.unwrap_or(""))); + provides.extend(f_provides.iter().map(|f| deb_feature2(&p, f))); + } + let provides_self = deb_feature(feature.unwrap_or("")); + // rust dropped Vec::remove_item for annoying reasons, the below is + // an unofficial recommended replacement from the RFC #40062 + let i = provides.iter().position(|x| *x == *provides_self); + i.map(|i| provides.remove(i)); + + let mut depends = vec!["${misc:Depends}".to_string()]; + if feature.is_some() && !f_deps.contains(&"") { + // in dh-cargo we symlink /usr/share/doc/{$feature => $main} pkg + // so we always need this direct dependency, even if the feature + // only indirectly depends on the bare library via another + depends.push(deb_feature("")); + } + depends.extend(f_deps.into_iter().map(deb_feature)); + depends.extend(o_deps); + + Ok(Package { + name: match feature { + None => deb_name(&pkgbase), + Some(f) => deb_feature_name(&pkgbase, f), + }, + arch: "any".to_string(), + // This is the best but not ideal option for us. + // + // Currently Debian M-A spec has a deficiency where a package X that + // build-depends on a (M-A:foreign+arch:all) package that itself + // depends on an arch:any package Z, will pick up the BUILD_ARCH of + // package Z instead of the HOST_ARCH. This is because we currently + // have no way of telling dpkg to use HOST_ARCH when checking that the + // dependencies of Y are satisfied, which is done at install-time + // without any knowledge that we're about to do a cross-compile. It + // is also problematic to tell dpkg to "accept any arch" because of + // the presence of non-M-A:same packages in the archive, that are not + // co-installable - different arches of Z might be depended-upon by + // two conflicting chains. (dpkg has so far chosen not to add an + // exception for the case where package Z is M-A:same co-installable). + // + // The recommended work-around for now from the dpkg developers is to + // make our packages arch:any M-A:same even though this results in + // duplicate packages in the Debian archive. For very large crates we + // will eventually want to make debcargo generate -data packages that + // are arch:all and have the arch:any -dev packages depend on it. + multi_arch: "same".to_string(), + section: None, + depends, + recommends, + suggests, + provides, + summary, + description, + extra_lines: match (name_suffix, feature) { + (Some(_), None) => { + let fullpkg = format!("{}-{}", basename, version); + vec![ + format!("Replaces: {}", deb_name(&fullpkg)), + format!("Breaks: {}", deb_name(&fullpkg)), + ] + } + (_, _) => vec![], + }, + }) + } + + pub fn new_bin( + basename: &str, + name_suffix: Option<&str>, + section: Option<&str>, + summary: Description, + description: Description, + ) -> Self { + let (name, mut provides) = match name_suffix { + None => (basename.to_string(), vec![]), + Some(suf) => ( + format!("{}{}", basename, suf), + vec![format!("{} (= ${{binary:Version}})", basename)], + ), + }; + provides.push("${cargo:Provides}".to_string()); + Package { + name, + arch: "any".to_string(), + multi_arch: "allowed".to_string(), + section: section.map(|s| s.to_string()), + depends: vec![ + "${misc:Depends}".to_string(), + "${shlibs:Depends}".to_string(), + "${cargo:Depends}".to_string(), + ], + recommends: vec!["${cargo:Recommends}".to_string()], + suggests: vec!["${cargo:Suggests}".to_string()], + provides, + summary, + description, + extra_lines: vec![ + "Built-Using: ${cargo:Built-Using}".to_string(), + "XB-X-Cargo-Built-Using: ${cargo:X-Cargo-Built-Using}".to_string(), + ], + } + } + + pub fn name(&self) -> &str { + self.name.as_str() + } + + fn write_description(&self, out: &mut fmt::Formatter) -> fmt::Result { + writeln!(out, "Description: {}", &self.summary)?; + let description = format!("{}", &self.description); + for line in fill(description.trim(), 79).lines() { + let line = line.trim_end(); + if line.is_empty() { + writeln!(out, " .")?; + } else if line.starts_with("- ") { + writeln!(out, " {}", line)?; + } else { + writeln!(out, " {}", line)?; + } + } + Ok(()) + } + + #[allow(clippy::result_unit_err)] + pub fn summary_check_len(&self) -> std::result::Result<(), ()> { + if self.summary.prefix.len() <= 80 { + Ok(()) + } else { + Err(()) + } + } + + pub fn apply_overrides(&mut self, config: &Config, key: PackageKey, f_provides: Vec<&str>) { + if let Some(section) = config.package_section(key) { + self.section = Some(section.to_string()); + } + self.summary + .apply_overrides(&config.summary, config.package_summary(key)); + self.description + .apply_overrides(&config.description, config.package_description(key)); + + self.depends.extend(config::package_field_for_feature( + &|x| config.package_depends(x), + key, + &f_provides, + )); + self.recommends.extend(config::package_field_for_feature( + &|x| config.package_recommends(x), + key, + &f_provides, + )); + self.suggests.extend(config::package_field_for_feature( + &|x| config.package_suggests(x), + key, + &f_provides, + )); + self.provides.extend(config::package_field_for_feature( + &|x| config.package_provides(x), + key, + &f_provides, + )); + + self.extra_lines.extend( + config + .package_extra_lines(key) + .into_iter() + .flatten() + .map(|s| s.to_string()), + ); + } +} + +impl Description { + fn apply_overrides(&mut self, global: &Option, per_package: Option<&str>) { + if let Some(per_package) = per_package { + self.prefix = per_package.to_string(); + self.suffix = "".to_string(); + } else if let Some(global) = &global { + self.prefix = global.into(); + } + } +} +impl fmt::Display for Description { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}{}", &self.prefix, self.suffix) + } +} + +impl PkgTest { + pub fn new( + name: &str, + crate_name: &str, + feature: &str, + version: &str, + extra_test_args: Vec<&str>, + depends: &[String], + extra_restricts: Vec<&str>, + ) -> Result { + Ok(PkgTest { + name: name.to_string(), + crate_name: crate_name.to_string(), + feature: feature.to_string(), + version: version.to_string(), + extra_test_args: extra_test_args.iter().map(|x| x.to_string()).collect(), + depends: depends.to_vec(), + extra_restricts: extra_restricts.iter().map(|x| x.to_string()).collect(), + }) + } +} + +/// Translates a semver into a Debian-format upstream version. +/// Omits the build metadata, and uses a ~ before the prerelease version so it +/// compares earlier than the subsequent release. +pub fn deb_upstream_version(v: &Version) -> String { + let mut s = format!("{}.{}.{}", v.major, v.minor, v.patch); + if !v.pre.is_empty() { + write!(s, "~{}", v.pre.as_str()).unwrap(); + } + s +} + +pub fn base_deb_name(crate_name: &str) -> String { + crate_name.replace('_', "-").to_lowercase() +} + +pub fn dsc_name(name: &str) -> String { + format!("{}-{}", Source::pkg_prefix(), base_deb_name(name)) +} + +pub fn deb_name(name: &str) -> String { + format!("{}-{}-dev", Package::pkg_prefix(), base_deb_name(name)) +} + +pub fn deb_feature_name(name: &str, feature: &str) -> String { + format!( + "{}-{}+{}-dev", + Package::pkg_prefix(), + base_deb_name(name), + base_deb_name(feature) + ) +} + +/// Retrieve one of a series of environment variables, and provide a friendly error message for +/// non-UTF-8 values. +fn get_envs(keys: &[&str]) -> Result> { + for key in keys { + match env::var(key) { + Ok(val) => { + return Ok(Some(val)); + } + Err(e @ VarError::NotUnicode(_)) => { + return Err(Error::from(e) + .context(format!("Environment variable ${} not valid UTF-8", key))); + } + Err(VarError::NotPresent) => {} + } + } + Ok(None) +} + +/// Determine a name and email address from environment variables. +pub fn get_deb_author() -> Result { + let name = get_envs(&["DEBFULLNAME", "NAME"])?.ok_or_else(|| { + format_err!("Unable to determine your name; please set $DEBFULLNAME or $NAME") + })?; + let email = get_envs(&["DEBEMAIL", "EMAIL"])?.ok_or_else(|| { + format_err!("Unable to determine your email; please set $DEBEMAIL or $EMAIL") + })?; + Ok(format!("{} <{}>", name, email)) +} + +#[cfg(test)] +mod tests; diff --git a/src/debian/control/tests.rs b/src/debian/control/tests.rs new file mode 100644 index 0000000..1ac0c7d --- /dev/null +++ b/src/debian/control/tests.rs @@ -0,0 +1,52 @@ +use super::PkgTest; + +struct PkgTestFmtData<'a> { + feature: &'a str, + extra_test_args: Vec<&'a str>, + depends: Vec, + extra_restricts: Vec<&'a str>, + expected: &'a str, +} + +#[test] +fn pkgtest_fmt_has_no_extra_whitespace() { + let checks = vec![ + PkgTestFmtData { + feature: "", + extra_test_args: Vec::new(), + depends: Vec::new(), + extra_restricts: Vec::new(), + expected: r"Test-Command: /usr/share/cargo/bin/cargo-auto-test crate 1.0 --all-targets +Features: test-name=librust-crate-dev: +Depends: dh-cargo (>= 18), @ +Restrictions: allow-stderr, skip-not-installable +", + }, + PkgTestFmtData { + feature: "X", + extra_test_args: vec!["--no-default-features", "--features X"], + depends: vec!["libfoo-dev".into(), "bar".into()], + extra_restricts: vec!["flaky"], + expected: r"Test-Command: /usr/share/cargo/bin/cargo-auto-test crate 1.0 --all-targets --no-default-features --features X +Features: test-name=librust-crate-dev:X +Depends: dh-cargo (>= 18), libfoo-dev, bar, @ +Restrictions: allow-stderr, skip-not-installable, flaky +", + }, + ]; + + for check in checks { + let pkgtest = PkgTest::new( + "librust-crate-dev", + "crate", + check.feature, + "1.0", + check.extra_test_args, + &check.depends, + check.extra_restricts, + ) + .unwrap(); + + assert_eq!(check.expected, &pkgtest.to_string()); + } +} diff --git a/src/debian/copyright.rs b/src/debian/copyright.rs new file mode 100644 index 0000000..6f2592f --- /dev/null +++ b/src/debian/copyright.rs @@ -0,0 +1,447 @@ +use cargo::core::manifest; +use chrono::{DateTime, Datelike, NaiveDateTime, Utc}; +use git2::Repository; +use regex; +use tempfile; +use textwrap::fill; +use walkdir; + +use std::cmp::Ordering; +use std::collections::BTreeMap; +use std::env; +use std::fmt; +use std::fs; +use std::io::{BufRead, BufReader, Read}; +use std::path::Path; + +use crate::errors::*; + +const DEB_COPYRIGHT_FORMAT: &str = "\ + https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/"; + +macro_rules! format_para { + ($fmt: expr, $text:expr) => {{ + for line in $text.lines() { + let line = line.trim_end(); + if line.is_empty() { + writeln!($fmt, " .")?; + } else { + writeln!($fmt, " {}", line)?; + } + } + write!($fmt, "") + }}; +} + +struct UpstreamInfo { + name: String, + contacts: Vec, + source: String, +} + +#[derive(Clone)] +pub struct Files { + files: String, + copyright: Vec, + license: String, + comment: String, +} + +#[derive(Clone)] +struct License { + name: String, + text: String, +} + +pub struct DebCopyright { + format: String, + upstream: UpstreamInfo, + files: Vec, + licenses: Vec, +} + +impl fmt::Display for DebCopyright { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "Format: {}", self.format)?; + write!(f, "{}", self.upstream)?; + + for file in &self.files { + write!(f, "\n{}", file)?; + } + + for license in &self.licenses { + write!(f, "\n{}", license)?; + } + + Ok(()) + } +} + +impl DebCopyright { + fn new(u: UpstreamInfo, f: &[Files], l: &[License]) -> DebCopyright { + DebCopyright { + format: DEB_COPYRIGHT_FORMAT.to_string(), + upstream: u, + files: f.to_vec(), + licenses: l.to_vec(), + } + } +} + +impl fmt::Display for UpstreamInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "Upstream-Name: {}", self.name)?; + write!(f, "Upstream-Contact:")?; + if self.contacts.len() > 1 { + writeln!(f)?; + } + for contact in &self.contacts { + writeln!(f, " {}", contact)?; + } + if !self.source.is_empty() { + writeln!(f, "Source: {}", self.source)?; + } + + Ok(()) + } +} + +impl UpstreamInfo { + fn new(name: String, authors: &[String], repo: &str) -> UpstreamInfo { + assert!(!authors.is_empty()); + UpstreamInfo { + name, + contacts: authors.to_vec(), + source: repo.to_string(), + } + } +} + +impl fmt::Display for Files { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "Files: {}", self.files)?; + write!(f, "Copyright:")?; + if self.copyright.len() > 1 { + writeln!(f)?; + } + for copyright in &self.copyright { + writeln!(f, " {}", copyright)?; + } + writeln!(f, "License: {}", self.license)?; + if !self.comment.is_empty() { + writeln!(f, "Comment:")?; + format_para!(f, &self.comment)?; + } + Ok(()) + } +} + +impl Files { + pub fn new(name: &str, notice: &[T], license: &str, comment: &str) -> Files { + assert!(!notice.is_empty()); + Files { + files: name.to_string(), + copyright: notice.iter().map(|s| s.to_string()).collect(), + license: license.to_string(), + comment: comment.to_string(), + } + } + + pub fn files(&self) -> &str { + &self.files + } + + pub fn license(&self) -> &str { + &self.license + } +} + +impl fmt::Display for License { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "License: {}", self.name)?; + format_para!(f, &self.text)?; + Ok(()) + } +} + +impl License { + fn new(name: String, text: String) -> License { + License { name, text } + } +} + +macro_rules! default_files { + ($file:expr, $notice:expr) => {{ + let comment = concat!( + "FIXME (overlay): These notices are extracted from files. Please ", + "review them before uploading to the archive." + ); + Files::new( + $file, + $notice, + "UNKNOWN-LICENSE; FIXME (overlay)", + &fill(comment, 79), + ) + }}; +} + +fn gen_files(debsrcdir: &Path) -> Result> { + let mut copyright_notices = BTreeMap::new(); + + let copyright_notice_re = + regex::Regex::new(r"(?:[Cc]opyright|©)(?:\s|[©:,()Cc<])*\b(\d{4}\b.*)$")?; + + // Get current working directory and move inside the extracted source of + // crate. This is necessary so as to capture correct path for files in + // debian/copyright. + let current_dir = env::current_dir()?; + env::set_current_dir(debsrcdir)?; + + // Here we specifically use "." to avoid absolute paths. If we use + // current_dir then we end up having absolute path from user home directory, + // which again messes debian/copyright. + // Use of . creates paths in format ./src/, so we strip the leading "./" + for entry in walkdir::WalkDir::new(".").sort_by(|a, b| a.file_name().cmp(b.file_name())) { + let entry = entry?; + if entry.file_type().is_file() { + let copyright_file = entry + .path() + .strip_prefix("./")? + .to_str() + .unwrap() + .to_string(); + let file = fs::File::open(entry.path())?; + let reader = BufReader::new(file); + for line in reader.lines() { + if let Ok(line) = line { + if let Some(m) = copyright_notice_re.captures(&line) { + let m = m.get(1).unwrap(); + let start = m.start(); + let end = m.end(); + let notice = line[start..end] + .trim_end() + .trim_end_matches(". See the COPYRIGHT") + .to_string(); + if !copyright_notices.contains_key(©right_file) { + copyright_notices.insert(copyright_file.clone(), vec![]); + } + copyright_notices + .get_mut(©right_file) + .unwrap() + .push(notice); + } + } else { + break; + } + } + } + } + + // Restore back to original working directory as we can continue without + // problems. + env::set_current_dir(current_dir.as_path())?; + + let mut notices: Vec = Vec::new(); + for (filename, notice) in ©right_notices { + notices.push(default_files!(filename, notice)); + } + + Ok(notices) +} + +fn get_licenses(license: &str) -> Result> { + let mut licenses = BTreeMap::new(); + let sep = regex::Regex::new(r"(?i:(\s(or|and)\s|/))")?; + let known_licenses = vec![ + ("agpl-3.0", include_str!("licenses/AGPL-3.0")), + ("apache-2.0", include_str!("licenses/Apache-2.0")), + ("bsd-2-clause", include_str!("licenses/BSD-2-Clause")), + ("bsd-3-clause", include_str!("licenses/BSD-3-Clause")), + ("cc0-1.0", include_str!("licenses/CC0-1.0")), + ("gpl-2.0", include_str!("licenses/GPL-2.0")), + ("gpl-3.0", include_str!("licenses/GPL-3.0")), + ("isc", include_str!("licenses/ISC")), + ("lgpl-2.0", include_str!("licenses/LGPL-2.0")), + ("lgpl-2.1", include_str!("licenses/LGPL-2.1")), + ("lgpl-3.0", include_str!("licenses/LGPL-3.0")), + ("mit", include_str!("licenses/MIT")), + ("mitnfa", include_str!("licenses/MITNFA")), + ("mpl-1.1", include_str!("licenses/MPL-1.1")), + ("mpl-2.0", include_str!("licenses/MPL-2.0")), + ("unlicense", include_str!("licenses/Unlicense")), + ("zlib", include_str!("licenses/Zlib")), + ] + .into_iter() + .collect::>(); + + let lses: Vec<&str> = sep.split(license).filter(|s| !s.is_empty()).collect(); + for ls in lses { + let lname = ls + .trim() + .to_lowercase() + .trim_end_matches('+') + .trim_end_matches("-or-later") + .to_string(); + let text = match known_licenses.get(lname.as_str()) { + Some(s) => s.to_string(), + None => "FIXME (overlay): Unrecognized crate license, please find the \ + full license text in the rest of the crate source code and \ + copy-paste it here" + .to_string(), + }; + licenses.insert(ls.trim().to_string(), text); + } + + let mut lblocks: Vec = Vec::new(); + if !licenses.is_empty() { + for (l, t) in licenses { + lblocks.push(License::new(l, t)); + } + } + + Ok(lblocks) +} + +fn copyright_fromgit(repo_url: &str) -> Result { + let tempdir = tempfile::Builder::new() + .prefix("debcargo") + .tempdir_in(".")?; + let repo = Repository::clone(repo_url, tempdir.path())?; + + let mut revwalker = repo.revwalk()?; + revwalker.push_head()?; + + // Get the latest and first commit id. This is bit ugly + let latest_id = revwalker.next().unwrap()?; + let first_id = revwalker.last().unwrap()?; // revwalker ends here is consumed by last + + let first_commit = repo.find_commit(first_id)?; + let latest_commit = repo.find_commit(latest_id)?; + + let first_year = DateTime::::from_utc( + NaiveDateTime::from_timestamp_opt(first_commit.time().seconds(), 0) + .ok_or(anyhow::Error::msg("lo"))?, + Utc, + ) + .year(); + + let latest_year = DateTime::::from_utc( + NaiveDateTime::from_timestamp_opt(latest_commit.time().seconds(), 0) + .ok_or(anyhow::Error::msg("lo"))?, + Utc, + ) + .year(); + + let notice = match first_year.cmp(&latest_year) { + Ordering::Equal => format!("{}", first_year), + _ => format!("{}-{},", first_year, latest_year), + }; + + Ok(notice) +} + +pub fn debian_copyright( + srcdir: &Path, + manifest: &manifest::Manifest, + manifest_path: &Path, + maintainer: &str, + uploaders: &[&str], + year_range: (i32, i32), + guess_harder: bool, +) -> Result { + let meta = manifest.metadata().clone(); + let repository = match meta.repository { + None => "", + Some(ref r) => r, + }; + + // The Authors field is optional according to + // https://rust-lang.github.io/rfcs/3052-optional-authors-field.html + // and crates.io publishes crates without the field already. + let unknown_authors = vec!["FIXME (overlay) UNKNOWN-AUTHORS".to_string()]; + let authors = if meta.authors.is_empty() { + &unknown_authors + } else { + &meta.authors + }; + + let upstream = UpstreamInfo::new(manifest.name().to_string(), authors, repository); + + let mut licenses: Vec = Vec::new(); + let mut crate_license: String = "".to_string(); + + if let Some(ref license_file_name) = meta.license_file { + let license_file = manifest_path.with_file_name(license_file_name); + let mut text = Vec::new(); + fs::File::open(license_file)?.read_to_end(&mut text)?; + licenses.reserve(1); + let stext = String::from_utf8(text)?; + licenses.push(License::new( + "UNKNOWN-LICENSE; FIXME (overlay)".to_string(), + stext, + )); + } else if let Some(ref license) = meta.license { + licenses = get_licenses(license).unwrap(); + crate_license = license + .trim() + .replace("/", " or ") + .replace(" OR ", " or ") + .replace(" AND ", " and "); + } else { + debcargo_bail!("Crate has no license or license_file"); + } + + let mut files = gen_files(srcdir)?; + + let (y0, y1) = year_range; + let years = if y0 == y1 { + format!("{}", y0) + } else { + format!("{}-{}", y0, y1) + }; + let mut deb_notice = vec![format!("{} {}", years, maintainer)]; + deb_notice.extend(uploaders.iter().map(|s| format!("{} {}", years, s))); + files.push(Files::new("debian/*", &deb_notice, &crate_license, "")); + + // Insert catch all block as the first block of copyright file. Capture + // copyright notice from git log of the upstream repository. + let years = if guess_harder && !repository.is_empty() { + match copyright_fromgit(repository) { + Ok(x) => x, + Err(e) => { + debcargo_warn!( + "Failed to generate d/copyright from git repository {}: {}\n", + repository, + e + ); + "FIXME (overlay) UNKNOWN-YEARS".to_string() + } + } + } else { + "FIXME (overlay) UNKNOWN-YEARS".to_string() + }; + let notice = match meta.authors.len() { + 0 => vec![format!("FIXME (overlay) UNKNOWN-AUTHORS {}", years)], + 1 => vec![format!("{} {}", years, &meta.authors[0])], + _ => meta + .authors + .iter() + .map(|s| format!("{} {}", years, s)) + .collect(), + }; + let comment = concat!( + "FIXME (overlay): Since upstream copyright years are not available ", + "in Cargo.toml, they were extracted from the upstream Git ", + "repository. This may not be correct information so you should ", + "review and fix this before uploading to the archive." + ); + files.insert( + 0, + Files::new("*", notice.as_slice(), &crate_license, &fill(comment, 79)), + ); + + Ok(DebCopyright::new(upstream, &files, &licenses)) +} + +#[cfg(test)] +mod tests; diff --git a/src/debian/copyright/tests.rs b/src/debian/copyright/tests.rs new file mode 100644 index 0000000..09a4ed2 --- /dev/null +++ b/src/debian/copyright/tests.rs @@ -0,0 +1,97 @@ +use super::{debian_copyright, get_licenses}; + +use std::path::Path; +use std::rc::Rc; + +use cargo::{ + core::{package::Package, SourceId}, + util::{config::Config, toml::TomlManifest}, +}; +use toml::toml; + +#[test] +fn check_get_licenses() { + let test_data: &[(&str, &[(&str, bool)])] = &[ + ("AGPL-3.0", &[("AGPL-3.0", true)]), + ("AcmeCorp-1.0", &[("AcmeCorp-1.0", false)]), + ("AGPL-3.0-or-later", &[("AGPL-3.0-or-later", true)]), + ("Apache-2.0/MIT", &[("Apache-2.0", true), ("MIT", true)]), + ("Apache-2.0 or MIT", &[("Apache-2.0", true), ("MIT", true)]), + ( + "FooBar-1.0 AND MIT", + &[("FooBar-1.0", false), ("MIT", true)], + ), + ]; + for (name, expected) in test_data { + let licenses = get_licenses(name).expect("getting licenses failed"); + let found: Vec<_> = licenses + .iter() + .map(|l| (l.name.as_str(), !l.text.starts_with("FIXME"))) + .collect(); + assert_eq!(&found[..], &expected[..]); + } +} + +#[test] +fn check_debian_copyright_authors() { + let checks = vec![ + ( + vec![], + vec!["FIXME (overlay) UNKNOWN-AUTHORS FIXME (overlay) UNKNOWN-YEARS"], + ), + ( + vec!["Jordan Doe"], + vec!["FIXME (overlay) UNKNOWN-YEARS Jordan Doe"], + ), + ( + vec!["Jordan Doe", "Jane Doe"], + vec![ + "FIXME (overlay) UNKNOWN-YEARS Jordan Doe", + "FIXME (overlay) UNKNOWN-YEARS Jane Doe", + ], + ), + ]; + + for (input, expected_output) in checks.into_iter() { + let package = build_package_with_authors(input); + let srcdir = tempfile::tempdir().unwrap(); + let copyright = debian_copyright( + srcdir.path(), + package.manifest(), + package.manifest_path(), + "Jordan Doe", + &[], + (2000, 2020), + false, + ) + .unwrap(); + let mut generated = false; + for file in ©right.files { + if file.files == "*" { + assert_eq!(file.copyright, expected_output); + generated = true; + } + } + assert!(generated); + } +} + +fn build_package_with_authors(authors: Vec<&str>) -> Package { + let authors: Vec = authors.into_iter().map(|s| s.to_string()).collect(); + let toml = toml! { + [package] + name = "mypackage" + version = "1.2.3" + authors = authors + license = "AGPLv3" + }; + let toml_manifest: Rc = + Rc::new(toml::from_str(&toml::to_string(&toml).unwrap()).unwrap()); + let source_id = SourceId::for_path(Path::new("/path/to/mypackage")).unwrap(); + let package_root = Path::new("/path/to/mypackage"); + let config = Config::default().unwrap(); + let manifest = TomlManifest::to_real_manifest(&toml_manifest, source_id, package_root, &config) + .unwrap() + .0; + Package::new(manifest, Path::new("/path/to/manifest")) +} diff --git a/src/debian/dependency.rs b/src/debian/dependency.rs new file mode 100644 index 0000000..c715aa6 --- /dev/null +++ b/src/debian/dependency.rs @@ -0,0 +1,327 @@ +use cargo::core::Dependency; +use itertools::Itertools; + +use std::cmp; +use std::fmt; + +use crate::config::{testing_ignore_debpolv, Config}; +use crate::debian::{self, control::base_deb_name, Package}; +use crate::errors::*; + +#[derive(Eq, Clone)] +#[allow(clippy::upper_case_acronyms)] +enum V { + M(u64), + MM(u64, u64), + MMP(u64, u64, u64), +} + +impl V { + fn new(p: &semver::Comparator) -> Result { + use self::V::*; + let mmp = match (p.minor, p.patch) { + (None, None) => M(p.major), + (Some(minor), None) => MM(p.major, minor), + (Some(minor), Some(patch)) => MMP(p.major, minor, patch), + (None, Some(_)) => debcargo_bail!("semver had patch without minor"), + }; + Ok(mmp) + } + + fn inclast(&self) -> V { + use self::V::*; + match *self { + M(major) => M(major + 1), + MM(major, minor) => MM(major, minor + 1), + MMP(major, minor, patch) => MMP(major, minor, patch + 1), + } + } + + fn mmp(&self) -> (u64, u64, u64) { + use self::V::*; + match *self { + M(major) => (major, 0, 0), + MM(major, minor) => (major, minor, 0), + MMP(major, minor, patch) => (major, minor, patch), + } + } +} + +impl cmp::Ord for V { + fn cmp(&self, other: &V) -> cmp::Ordering { + self.partial_cmp(other).unwrap() + } +} + +impl cmp::PartialOrd for V { + fn partial_cmp(&self, other: &V) -> Option { + self.mmp().partial_cmp(&other.mmp()) + } +} + +impl cmp::PartialEq for V { + fn eq(&self, other: &V) -> bool { + self.mmp() == other.mmp() + } +} + +impl fmt::Display for V { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::V::*; + match *self { + M(major) => write!(f, "{}", major), + MM(major, minor) => write!(f, "{}.{}", major, minor), + MMP(major, minor, patch) => write!(f, "{}.{}.{}", major, minor, patch), + } + } +} + +struct VRange { + ge: Option, + lt: Option, +} + +impl VRange { + fn new() -> Self { + VRange { ge: None, lt: None } + } + + fn constrain_ge(&mut self, ge: V) -> &Self { + match self.ge { + Some(ref ge_) if &ge < ge_ => (), + _ => self.ge = Some(ge), + }; + self + } + + fn constrain_lt(&mut self, lt: V) -> &Self { + match self.lt { + Some(ref lt_) if < >= lt_ => (), + _ => self.lt = Some(lt), + }; + self + } + + fn to_deb_or_clause(&self, base: &str, suffix: &str) -> Result { + use debian::dependency::V::*; + match (&self.ge, &self.lt) { + (None, None) => Ok(format!("{}{}", base, suffix)), + (Some(ge), None) => Ok(format!("{}{} (>= {}-~~)", base, suffix, ge)), + (None, Some(lt)) => Ok(format!("{}{} (<< {}-~~)", base, suffix, lt)), + (Some(ge), Some(lt)) => { + if ge >= lt { + debcargo_bail!("bad version range: >= {}, << {}", ge, lt); + } + let mut ranges = vec![]; + let (lt_maj, lt_min, lt_pat) = lt.mmp(); + let (ge_maj, ge_min, ge_pat) = ge.mmp(); + if ge_maj < lt_maj { + ranges.push((M(ge_maj), Some((true, ge)))); + ranges.extend((ge_maj + 1..lt_maj).map(|maj| (M(maj), None))); + ranges.push((M(lt_maj), Some((false, lt)))); + } else { + assert!(ge_maj == lt_maj); + if ge_min < lt_min { + ranges.push((MM(ge_maj, ge_min), Some((true, ge)))); + ranges.extend((ge_min + 1..lt_min).map(|min| (MM(ge_maj, min), None))); + ranges.push((MM(lt_maj, lt_min), Some((false, lt)))); + } else { + assert!(ge_min == lt_min); + ranges.push((MMP(ge_maj, ge_min, ge_pat), Some((true, ge)))); + ranges.extend( + (ge_pat + 1..lt_pat).map(|pat| (MMP(ge_maj, ge_min, pat), None)), + ); + ranges.push((MMP(lt_maj, lt_min, lt_pat), Some((false, lt)))); + } + }; + // reverse the order so higher versions go first + // this helps sbuild find build-deps, it does not resolve alternatives by default + Ok(ranges + .iter() + .rev() + .filter_map(|(ver, cons)| match cons { + None => Some(format!("{}-{}{}", base, ver, suffix)), + Some((true, c)) => { + if c == &ver { + // A-x >= x is redundant, drop the >= + Some(format!("{}-{}{}", base, ver, suffix)) + } else { + Some(format!("{}-{}{} (>= {}-~~)", base, ver, suffix, c)) + } + } + Some((false, c)) => { + if c == &ver { + // A-x << x is unsatisfiable, drop it + None + } else { + Some(format!("{}-{}{} (<< {}-~~)", base, ver, suffix, c)) + } + } + }) + .join(" | ")) + } + } + } +} + +fn coerce_unacceptable_predicate<'a>( + dep: &Dependency, + p: &'a semver::Comparator, + allow_prerelease_deps: bool, +) -> Result<&'a semver::Op> { + let mmp = &V::new(p)?; + + // Cargo/semver and Debian handle pre-release versions quite + // differently, so a versioned Debian dependency cannot properly + // handle pre-release crates. This might be OK most of the time, + // coerce it to the non-pre-release version. + if !p.pre.is_empty() { + if allow_prerelease_deps || testing_ignore_debpolv() { + debcargo_warn!( + "Coercing removal of prerelease part of dependency: {} {:?}", + dep.package_name(), + p + ) + } else { + debcargo_bail!( + "Cannot represent prerelease part of dependency: {} {:?}", + dep.package_name(), + p + ) + } + } + + use debian::dependency::V::M; + use semver::Op::*; + match (&p.op, mmp) { + (&Greater, &M(0)) => Ok(&p.op), + (&GreaterEq, &M(0)) => { + debcargo_warn!( + "Coercing unrepresentable dependency version predicate 'GtEq 0' to 'Gt 0': {} {:?}", + dep.package_name(), + p + ); + Ok(&Greater) + } + // TODO: This will prevent us from handling wildcard dependencies with + // 0.0.0* so for now commenting this out. + // (_, &M(0)) => debcargo_bail!( + // "Unrepresentable dependency version predicate: {} {:?}", + // dep.package_name(), + // p + // ), + (_, _) => Ok(&p.op), + } +} + +fn generate_version_constraints( + vr: &mut VRange, + dep: &Dependency, + p: &semver::Comparator, + op: &semver::Op, +) -> Result<()> { + let mmp = V::new(p)?; + use debian::dependency::V::*; + use semver::Op::*; + // see https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html + // and https://docs.rs/semver/1/semver/enum.Op.html for semantics + match (*op, &mmp) { + (Less, &M(0)) | (Less, &MM(0, 0)) | (Less, &MMP(0, 0, 0)) => debcargo_bail!( + "Unrepresentable dependency version predicate: {} {:?}", + dep.package_name(), + p + ), + (Less, _) => { + vr.constrain_lt(mmp); + } + (LessEq, _) => { + vr.constrain_lt(mmp.inclast()); + } + (Greater, _) => { + vr.constrain_ge(mmp.inclast()); + } + (GreaterEq, _) => { + vr.constrain_ge(mmp); + } + (Exact, _) | (Wildcard, _) => { + vr.constrain_lt(mmp.inclast()); + vr.constrain_ge(mmp); + } + (Tilde, &M(_)) | (Tilde, &MM(_, _)) => { + vr.constrain_lt(mmp.inclast()); + vr.constrain_ge(mmp); + } + (Tilde, &MMP(major, minor, _)) => { + vr.constrain_lt(MM(major, minor + 1)); + vr.constrain_ge(mmp); + } + + (Caret, &MMP(0, 0, _)) => { + vr.constrain_lt(mmp.inclast()); + vr.constrain_ge(mmp); + } + (Caret, &MMP(0, minor, _)) | (Caret, &MM(0, minor)) => { + vr.constrain_lt(MM(0, minor + 1)); + vr.constrain_ge(mmp); + } + (Caret, &MMP(major, _, _)) | (Caret, &MM(major, _)) | (Caret, &M(major)) => { + vr.constrain_lt(M(major + 1)); + vr.constrain_ge(mmp); + } + + (_, _) => { + // https://github.com/dtolnay/semver/issues/262 + panic!("Op is non-exhuastive for some reason"); + } + } + + Ok(()) +} + +/// Translates a Cargo dependency into a Debian package dependency. +pub fn deb_dep(config: &Config, dep: &Dependency) -> Result> // result is a AND-clause +{ + let dep_dashed = base_deb_name(&dep.package_name()); + let mut suffixes = Vec::new(); + if dep.uses_default_features() { + suffixes.push("+default-dev".to_string()); + } + for feature in dep.features() { + suffixes.push(format!("+{}-dev", base_deb_name(feature))); + } + if suffixes.is_empty() { + suffixes.push("-dev".to_string()); + } + let req = semver::VersionReq::parse(&dep.version_req().to_string()).unwrap(); + let mut deps = Vec::new(); + for suffix in suffixes { + let base = format!("{}-{}", Package::pkg_prefix(), dep_dashed); + let mut vr = VRange::new(); + for p in &req.comparators { + let op = coerce_unacceptable_predicate(dep, p, config.allow_prerelease_deps)?; + generate_version_constraints(&mut vr, dep, p, op)?; + } + deps.push(vr.to_deb_or_clause(&base, &suffix)?); + } + Ok(deps) +} + +pub fn deb_deps(config: &Config, cdeps: &[Dependency]) -> Result> // result is a AND-clause +{ + let mut deps = Vec::new(); + for dep in cdeps { + deps.extend(deb_dep(config, dep)?.iter().map(String::to_string)); + } + deps.sort(); + deps.dedup(); + Ok(deps) +} + +pub fn deb_dep_add_nocheck(x: &str) -> String { + x.to_string() + .split('|') + .map(|x| x.trim_end().to_string() + " ") + .join("|") + .trim_end() + .to_string() +} diff --git a/src/debian/licenses/AGPL-3.0 b/src/debian/licenses/AGPL-3.0 new file mode 100644 index 0000000..dba13ed --- /dev/null +++ b/src/debian/licenses/AGPL-3.0 @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/src/debian/licenses/Apache-2.0 b/src/debian/licenses/Apache-2.0 new file mode 100644 index 0000000..d69cdd1 --- /dev/null +++ b/src/debian/licenses/Apache-2.0 @@ -0,0 +1,2 @@ +Debian systems provide the Apache 2.0 license in +/usr/share/common-licenses/Apache-2.0 diff --git a/src/debian/licenses/BSD-2-Clause b/src/debian/licenses/BSD-2-Clause new file mode 100644 index 0000000..d17fad3 --- /dev/null +++ b/src/debian/licenses/BSD-2-Clause @@ -0,0 +1,20 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/debian/licenses/BSD-3-Clause b/src/debian/licenses/BSD-3-Clause new file mode 100644 index 0000000..689c723 --- /dev/null +++ b/src/debian/licenses/BSD-3-Clause @@ -0,0 +1,24 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/debian/licenses/CC0-1.0 b/src/debian/licenses/CC0-1.0 new file mode 100644 index 0000000..e37e562 --- /dev/null +++ b/src/debian/licenses/CC0-1.0 @@ -0,0 +1,2 @@ +Debian systems provide the CC0 1.0 Universal License in +/usr/share/common-licenses/CC0-1.0 diff --git a/src/debian/licenses/GPL-2.0 b/src/debian/licenses/GPL-2.0 new file mode 100644 index 0000000..0c94208 --- /dev/null +++ b/src/debian/licenses/GPL-2.0 @@ -0,0 +1 @@ +Debian systems provide the GPL 2.0 in /usr/share/common-licenses/GPL-2 diff --git a/src/debian/licenses/GPL-3.0 b/src/debian/licenses/GPL-3.0 new file mode 100644 index 0000000..4e80768 --- /dev/null +++ b/src/debian/licenses/GPL-3.0 @@ -0,0 +1 @@ +Debian systems provide the GPL 3.0 in /usr/share/common-licenses/GPL-3 diff --git a/src/debian/licenses/ISC b/src/debian/licenses/ISC new file mode 100644 index 0000000..8351a30 --- /dev/null +++ b/src/debian/licenses/ISC @@ -0,0 +1,11 @@ +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/src/debian/licenses/LGPL-2.0 b/src/debian/licenses/LGPL-2.0 new file mode 100644 index 0000000..5fa5065 --- /dev/null +++ b/src/debian/licenses/LGPL-2.0 @@ -0,0 +1 @@ +Debian systems provide the LGPL 2.0 in /usr/share/common-licenses/LGPL-2 diff --git a/src/debian/licenses/LGPL-2.1 b/src/debian/licenses/LGPL-2.1 new file mode 100644 index 0000000..32f41bb --- /dev/null +++ b/src/debian/licenses/LGPL-2.1 @@ -0,0 +1 @@ +Debian systems provide the LGPL 2.1 in /usr/share/common-licenses/LGPL-2.1 diff --git a/src/debian/licenses/LGPL-3.0 b/src/debian/licenses/LGPL-3.0 new file mode 100644 index 0000000..91e41a7 --- /dev/null +++ b/src/debian/licenses/LGPL-3.0 @@ -0,0 +1 @@ +Debian systems provide the LGPL 3.0 in /usr/share/common-licenses/LGPL-3 diff --git a/src/debian/licenses/MIT b/src/debian/licenses/MIT new file mode 100644 index 0000000..50b3cf0 --- /dev/null +++ b/src/debian/licenses/MIT @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/debian/licenses/MITNFA b/src/debian/licenses/MITNFA new file mode 100644 index 0000000..8eff3c8 --- /dev/null +++ b/src/debian/licenses/MITNFA @@ -0,0 +1,29 @@ +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +Distributions of all or part of the Software intended to be used +by the recipients as they would use the unmodified Software, +containing modifications that substantially alter, remove, or +disable functionality of the Software, outside of the documented +configuration mechanisms provided by the Software, shall be +modified such that the Original Author's bug reporting email +addresses and urls are either replaced with the contact information +of the parties responsible for the changes, or removed entirely. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/debian/licenses/MPL-1.1 b/src/debian/licenses/MPL-1.1 new file mode 100644 index 0000000..f07159d --- /dev/null +++ b/src/debian/licenses/MPL-1.1 @@ -0,0 +1 @@ +Debian systems provide the MPL 1.1 in /usr/share/common-licenses/MPL-1.1 diff --git a/src/debian/licenses/MPL-2.0 b/src/debian/licenses/MPL-2.0 new file mode 100644 index 0000000..8dcf8f1 --- /dev/null +++ b/src/debian/licenses/MPL-2.0 @@ -0,0 +1 @@ +Debian systems provide the MPL 2.0 in /usr/share/common-licenses/MPL-2.0 diff --git a/src/debian/licenses/Unlicense b/src/debian/licenses/Unlicense new file mode 100644 index 0000000..b3dbff0 --- /dev/null +++ b/src/debian/licenses/Unlicense @@ -0,0 +1,22 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/debian/licenses/Zlib b/src/debian/licenses/Zlib new file mode 100644 index 0000000..480c61e --- /dev/null +++ b/src/debian/licenses/Zlib @@ -0,0 +1,15 @@ +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff --git a/src/debian/mod.rs b/src/debian/mod.rs new file mode 100644 index 0000000..27e070d --- /dev/null +++ b/src/debian/mod.rs @@ -0,0 +1,1216 @@ +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::fs; +use std::io::{self, ErrorKind, Read, Seek, Write as IoWrite}; +use std::os::unix::fs::PermissionsExt; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::str::FromStr; + +use anyhow::format_err; +use chrono::{self, Datelike}; +use flate2::read::GzDecoder; +use flate2::write::GzEncoder; +use flate2::Compression; +use regex::Regex; +use tar::{Archive, Builder}; +use tempfile; + +use crate::config::{package_field_for_feature, testing_ignore_debpolv, Config, PackageKey}; +use crate::crates::{show_dep, transitive_deps, CrateDepInfo, CrateInfo}; +use crate::errors::*; +use crate::util::{self, copy_tree, expect_success, get_transitive_val, traverse_depth}; + +use self::changelog::{ChangelogEntry, ChangelogIterator}; +use self::control::{base_deb_name, deb_upstream_version}; +use self::control::{Description, Package, PkgTest, Source}; +use self::copyright::debian_copyright; +pub use self::dependency::{deb_dep_add_nocheck, deb_deps}; + +pub mod changelog; +pub mod control; +pub mod copyright; +mod dependency; + +pub struct DebInfo { + upstream_name: String, + /// Debian package name without rust- prefix or any semver suffix + base_package_name: String, + /// Package name suffix after the base package name. + /// Some implies semver_suffix, i.e. Some("") is different from None + name_suffix: Option, + uscan_version_pattern: Option, + /// Debian package name without rust- prefix + package_name: String, + deb_upstream_version: String, + debcargo_version: String, + package_source_dir: PathBuf, + orig_tarball_path: PathBuf, +} + +impl DebInfo { + pub fn new(crate_info: &CrateInfo, debcargo_version: &str, semver_suffix: bool) -> Self { + let upstream_name = crate_info.package_id().name().to_string(); + let name_dashed = base_deb_name(&upstream_name); + let base_package_name = name_dashed.to_lowercase(); + let (name_suffix, uscan_version_pattern, package_name) = if semver_suffix { + let semver = crate_info.semver(); + let name_suffix = format!("-{}", &semver); + // See `man uscan` description of @ANY_VERSION@ on how these + // regex patterns were built. + let uscan = format!("[-_]?({}\\.\\d[\\-+\\.:\\~\\da-zA-Z]*)", &semver); + let pkgname = format!("{}{}", base_package_name, &name_suffix); + (Some(name_suffix), Some(uscan), pkgname) + } else { + (None, None, base_package_name.clone()) + }; + let deb_upstream_version = deb_upstream_version(crate_info.version()); + let package_source_dir = PathBuf::from(format!( + "{}-{}-{}", + Source::pkg_prefix(), + package_name, + deb_upstream_version + )); + let orig_tarball_path = PathBuf::from(format!( + "{}-{}_{}.orig.tar.gz", + Source::pkg_prefix(), + package_name, + deb_upstream_version + )); + + DebInfo { + upstream_name, + base_package_name, + name_suffix, + uscan_version_pattern, + package_name, + deb_upstream_version, + debcargo_version: debcargo_version.to_string(), + package_source_dir, + orig_tarball_path, + } + } + + pub fn upstream_name(&self) -> &str { + self.upstream_name.as_str() + } + + pub fn base_package_name(&self) -> &str { + self.base_package_name.as_str() + } + + pub fn name_suffix(&self) -> Option<&str> { + self.name_suffix.as_deref() + } + + pub fn package_name(&self) -> &str { + self.package_name.as_str() + } + + pub fn deb_upstream_version(&self) -> &str { + self.deb_upstream_version.as_str() + } + + pub fn debcargo_version(&self) -> &str { + self.debcargo_version.as_str() + } + + pub fn package_source_dir(&self) -> &Path { + self.package_source_dir.as_ref() + } + + pub fn orig_tarball_path(&self) -> &Path { + self.orig_tarball_path.as_ref() + } +} + +pub fn prepare_orig_tarball( + crate_info: &CrateInfo, + tarball: &Path, + src_modified: bool, + output_dir: &Path, +) -> Result<()> { + let crate_file = crate_info.crate_file(); + let tempdir = tempfile::Builder::new() + .prefix("debcargo") + .tempdir_in(".")?; + let temp_archive_path = tempdir.path().join(tarball); + + let mut create = fs::OpenOptions::new(); + create.write(true).create_new(true); + + if src_modified { + debcargo_info!("crate tarball was modified; repacking for debian"); + let mut f = crate_file.file(); + f.seek(io::SeekFrom::Start(0))?; + let mut archive = Archive::new(GzDecoder::new(f)); + let mut new_archive = Builder::new(GzEncoder::new( + create.open(&temp_archive_path)?, + Compression::best(), + )); + + for entry in archive.entries()? { + let entry = entry?; + let path = entry.path()?.into_owned(); + if path.ends_with("Cargo.toml") && path.iter().count() == 2 { + // Put the rewritten and original Cargo.toml back into the orig tarball + let mut new_archive_append = |name: &str| { + let mut header = entry.header().clone(); + let srcpath = output_dir.join(name); + header.set_path(path.parent().unwrap().join(name))?; + header.set_size(fs::metadata(&srcpath)?.len()); + header.set_cksum(); + new_archive.append(&header, fs::File::open(&srcpath)?) + }; + new_archive_append("Cargo.toml")?; + new_archive_append("Cargo.toml.orig")?; + } else { + match crate_info.filter_path(&entry.path()?) { + Err(e) => debcargo_bail!(e), + Ok(r) => { + if !r { + new_archive.append(&entry.header().clone(), entry)?; + } else { + writeln!( + io::stderr(), + "Filtered out files from .orig.tar.gz: {:?}", + &entry.path()? + )?; + } + } + } + } + } + + new_archive.finish()?; + } else { + fs::copy(crate_file.path(), &temp_archive_path)?; + } + + fs::rename(temp_archive_path, &tarball)?; + Ok(()) +} + +pub fn apply_overlay_and_patches( + crate_info: &mut CrateInfo, + config_path: Option<&Path>, + config: &Config, + output_dir: &Path, +) -> Result { + let tempdir = tempfile::Builder::new() + .prefix("debcargo") + .tempdir_in(".")?; + let overlay = config.overlay_dir(config_path); + if let Some(p) = overlay.as_ref() { + for anc in tempdir.path().ancestors() { + if p.as_path() == anc { + debcargo_bail!( + "Aborting: refusing to copy an ancestor {} into a descendant {}", + p.as_path().display(), + tempdir.path().display(), + ); + } + } + copy_tree(p.as_path(), tempdir.path()).unwrap(); + } + if tempdir.path().join("control").exists() { + debcargo_warn!( + "Most of the time you shouldn't overlay debian/control, \ +it's a maintenance burden. Use debcargo.toml instead." + ) + } + if tempdir.path().join("patches").join("series").exists() { + // apply patches to Cargo.toml in case they exist, and re-read it + let output_dir = &fs::canonicalize(&output_dir)?; + let stderr = || { + // create a new owned handle to stderr + fs::OpenOptions::new() + .append(true) + .open("/dev/stderr") + .unwrap() + }; + expect_success( + Command::new("quilt") + .stdout(stderr()) + .current_dir(&output_dir) + .env("QUILT_PATCHES", tempdir.path().join("patches")) + .args(&["push", "--quiltrc=-", "-a"]), + "failed to apply patches using quilt", + ); + crate_info.replace_manifest(&output_dir.join("Cargo.toml"))?; + expect_success( + Command::new("quilt") + .stdout(stderr()) + .current_dir(&output_dir) + .env("QUILT_PATCHES", tempdir.path().join("patches")) + .args(&["pop", "--quiltrc=-", "-a"]), + "failed to unapply patches", + ); + } + Ok(tempdir) +} + +#[allow(clippy::too_many_arguments)] +pub fn prepare_debian_folder( + crate_info: &mut CrateInfo, + deb_info: &DebInfo, + config_path: Option<&Path>, + config: &Config, + output_dir: &Path, + tempdir: &tempfile::TempDir, + changelog_ready: bool, + copyright_guess_harder: bool, + overlay_write_back: bool, +) -> Result<()> { + let mut create = fs::OpenOptions::new(); + create.write(true).create_new(true); + + let crate_name = crate_info.package_id().name(); + let crate_version = crate_info.package_id().version(); + let upstream_name = deb_info.upstream_name(); + + let maintainer = config.maintainer(); + let uploaders: Vec<&str> = config + .uploaders() + .into_iter() + .flatten() + .map(String::as_str) + .collect(); + + let mut new_hints = vec![]; + let mut file = |name: &str| { + let path = tempdir.path(); + let f = path.join(name); + fs::create_dir_all(f.parent().unwrap())?; + create.open(&f).or_else(|e| match e.kind() { + ErrorKind::AlreadyExists => { + let hintname = name.to_owned() + util::HINT_SUFFIX; + let hint = path.join(&hintname); + if hint.exists() { + fs::remove_file(&hint)?; + } + new_hints.push(hintname); + create.open(&hint) + } + _ => Err(e), + }) + }; + + // debian/cargo-checksum.json + { + let checksum = crate_info + .checksum() + .unwrap_or("Could not get crate checksum"); + let mut cargo_checksum_json = file("cargo-checksum.json")?; + writeln!( + cargo_checksum_json, + r#"{{"package":"{}","files":{{}}}}"#, + checksum + )?; + } + + // debian/compat + { + let mut compat = file("compat")?; + writeln!(compat, "12")?; + } + + // debian/copyright + { + let mut copyright = io::BufWriter::new(file("copyright")?); + let year_range = if changelog_ready { + // if changelog is ready, unconditionally read the year range from it + changelog_first_last(tempdir.path())? + } else { + // otherwise use the first date if it exists + let last = chrono::Local::now().year(); + match changelog_first_last(tempdir.path()) { + Ok((first, _)) => (first, last), + Err(_) => (last, last), + } + }; + let dep5_copyright = debian_copyright( + output_dir, + crate_info.manifest(), + crate_info.manifest_path(), + maintainer, + &uploaders, + year_range, + copyright_guess_harder, + )?; + write!(copyright, "{}", dep5_copyright)?; + } + + // debian/watch + { + let mut watch = file("watch")?; + match config.crate_src_path(config_path) { + Some(_) => write!(watch, "FIXME add uscan directive for local crate")?, + None => { + let uscan_version_pattern = deb_info + .uscan_version_pattern + .as_ref() + .map_or_else(|| "@ANY_VERSION@".to_string(), |ref s| s.to_string()); + writeln!(watch, "version=4")?; + writeln!( + watch, + r"opts=filenamemangle=s/.*\/(.*)\/download/{name}-$1\.tar\.gz/g,\", + name = upstream_name + )?; + writeln!( + watch, + r"uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha)\d*)$/$1~$2/ \" + )?; + writeln!( + watch, + "https://qa.debian.org/cgi-bin/fakeupstream.cgi?upstream=crates.io/{name} \ + .*/crates/{name}/{version_pattern}/download", + name = upstream_name, + version_pattern = uscan_version_pattern + )?; + } + }; + } + + // debian/source/format + { + fs::create_dir_all(tempdir.path().join("source"))?; + let mut source_format = file("source/format")?; + writeln!(source_format, "3.0 (quilt)")?; + } + + // debian/control & debian/tests/control + let (source, has_dev_depends, default_test_broken) = + prepare_debian_control(deb_info, crate_info, config, &mut file)?; + + // for testing only, debian/debcargo_testing_bin/env + if testing_ignore_debpolv() { + fs::create_dir_all(tempdir.path().join("debcargo_testing_bin"))?; + let mut env_hack = file("debcargo_testing_bin/env")?; + env_hack.set_permissions(fs::Permissions::from_mode(0o777))?; + // intercept calls to dh-cargo-built-using + writeln!( + env_hack, + r#"#!/bin/sh +case "$*" in */usr/share/cargo/bin/dh-cargo-built-using*) +echo "debcargo testing: suppressing dh-cargo-built-using";; +*) /usr/bin/env "$@";; esac +"# + )?; + } + + // debian/rules + { + let mut rules = file("rules")?; + rules.set_permissions(fs::Permissions::from_mode(0o777))?; + if has_dev_depends || testing_ignore_debpolv() { + // don't run any tests, we don't want extra B-D on dev-depends + // this could potentially cause B-D cycles so we avoid it + // + // also don't run crate tests during integration testing since some + // of them are brittle and fail; the purpose is to test debcargo + // not the actual crates + write!( + rules, + "{}", + concat!( + "#!/usr/bin/make -f\n", + "%:\n", + "\tdh $@ --buildsystem cargo\n" + ) + )?; + // some crates need nightly to compile, annoyingly. only do this in + // testing; outside of testing the user should explicitly override + // debian/rules to do this + if testing_ignore_debpolv() { + writeln!(rules, "export RUSTC_BOOTSTRAP := 1")?; + writeln!( + rules, + "export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH)" + )?; + } + } else { + write!( + rules, + "{}{}", + concat!( + "#!/usr/bin/make -f\n", + "%:\n", + "\tdh $@ --buildsystem cargo\n", + "\n", + "override_dh_auto_test:\n", + ), + // TODO: this logic is slightly brittle if another feature + // "provides" the default feature. In this case, you need to + // set test_is_broken explicitly on package."lib+default" and + // not package."lib+theotherfeature". + if default_test_broken { + "\tdh_auto_test -- test --all || true\n" + } else { + "\tdh_auto_test -- test --all\n" + }, + )?; + } + } + + // debian/changelog + if !changelog_ready { + let author = control::get_deb_author()?; + let crate_src = match config.crate_src_path(config_path) { + Some(_) => "local source", + None => "crates.io", + }; + let autogenerated_item = format!( + " * Package {} {} from {} using debcargo {}", + &crate_name, + &crate_version, + &crate_src, + deb_info.debcargo_version() + ); + let autogenerated_re = Regex::new(&format!( + r"^ \* Package (.*) (.*) from {} using debcargo (.*)$", + &crate_src + )) + .unwrap(); + + // Special-case d/changelog: + let (mut changelog, changelog_data) = changelog_or_new(tempdir.path())?; + let (changelog_old, mut changelog_items, deb_version_suffix) = { + let ver_bump = &|e: &Option<&str>| -> Result> { + Ok(match e { + Some(x) => { + let e = ChangelogEntry::from_str(x)?; + if e.version_parts().0 == deb_info.deb_upstream_version() { + Some(e.deb_version_suffix_bump()) + } else { + None + } + } + None => None, + }) + }; + let mut chit = ChangelogIterator::from(&changelog_data); + let e1 = chit.next(); + match e1 { + // If the first entry has changelog::DEFAULT_DIST then write over it smartly + Some(x) if x.contains(changelog::DEFAULT_DIST) => { + let mut e = ChangelogEntry::from_str(x)?; + if author == e.maintainer { + if let Some(pos) = e.items.iter().position(|x| autogenerated_re.is_match(x)) + { + e.items[pos] = autogenerated_item; + } else { + e.items.push(autogenerated_item); + } + } else { + // If unreleased changelog is by someone else, preserve their entries + e.items.insert(0, autogenerated_item); + e.items.insert(1, "".to_string()); + let ename = e.maintainer_name(); + e.items.insert(2, format!(" [ {} ]", ename)); + } + (&changelog_data[x.len()..], e.items, ver_bump(&chit.next())?) + } + // Otherwise prepend a new entry to the existing entries + _ => ( + changelog_data.as_str(), + vec![autogenerated_item], + ver_bump(&e1)?, + ), + } + }; + + let source_deb_version = format!( + "{}-{}", + deb_info.deb_upstream_version(), + &deb_version_suffix.unwrap_or_else(|| "1".to_string()) + ); + if !uploaders.contains(&author.as_str()) { + debcargo_warn!( + "You ({}) are not in Uploaders; adding \"Team upload\" to d/changelog", + author + ); + if !changelog_items.contains(&changelog::COMMENT_TEAM_UPLOAD.to_string()) { + changelog_items.insert(0, changelog::COMMENT_TEAM_UPLOAD.to_string()); + } + } + let changelog_new_entry = ChangelogEntry::new( + source.name().to_string(), + source_deb_version, + changelog::DEFAULT_DIST.to_string(), + "urgency=medium".to_string(), + author, + changelog::local_now(), + changelog_items, + ); + + changelog.seek(io::SeekFrom::Start(0))?; + if changelog_old.is_empty() { + write!(changelog, "{}", changelog_new_entry)?; + } else { + write!(changelog, "{}\n{}", changelog_new_entry, changelog_old)?; + } + // the new file might be shorter, truncate it to the current cursor position + let pos = changelog.seek(io::SeekFrom::Current(0))?; + changelog.set_len(pos)?; + } + + if overlay_write_back { + let overlay = config.overlay_dir(config_path); + if let Some(p) = overlay.as_ref() { + if !changelog_ready { + // Special-case d/changelog: + // Always write it back, this is safe because of our prepending logic + new_hints.push("changelog".to_string()); + } + for hint in &new_hints { + let newpath = tempdir.path().join(hint); + let oldpath = p.join(hint); + fs::copy(newpath, oldpath).expect("could not write back"); + debcargo_info!("Wrote back file to overlay: {}", hint); + } + } + } + + fs::rename(tempdir.path(), output_dir.join("debian"))?; + Ok(()) +} + +fn prepare_debian_control std::result::Result>( + deb_info: &DebInfo, + crate_info: &CrateInfo, + config: &Config, + mut file: F, +) -> Result<(Source, bool, bool)> { + let crate_name = crate_info.crate_name(); + let deb_upstream_version = deb_info.deb_upstream_version(); + let base_pkgname = deb_info.base_package_name(); + let name_suffix = deb_info.name_suffix(); + + let lib = crate_info.is_lib(); + let mut bins = crate_info.get_binary_targets(); + if lib && !bins.is_empty() && !config.build_bin_package() { + bins.clear(); + } + let bin_name = if config.bin_name.eq(&Config::default().bin_name) { + let default_bin_name = deb_info.base_package_name(); + if !bins.is_empty() { + debcargo_info!( + "Generate binary crate with default name '{}', set bin_name to override or bin = false to disable.", + &default_bin_name + ); + } + default_bin_name + } else { + config.bin_name.as_str() + }; + + let maintainer = config.maintainer(); + let requires_root = config.requires_root(); + let uploaders: Vec<&str> = config + .uploaders() + .into_iter() + .flatten() + .map(String::as_str) + .collect(); + + let features_with_deps = crate_info.all_dependencies_and_features(); + let dev_depends = deb_deps(config, &crate_info.dev_dependencies())?; + log::trace!( + "features_with_deps: {:?}", + features_with_deps + .iter() + .map(|(&f, &(ref ff, ref dd))| { + (f, (ff, dd.iter().map(show_dep).collect::>())) + }) + .collect::>() + ); + let meta = crate_info.metadata(); + + // debian/tests/control, preparation + let test_is_marked_broken = |f: &str| config.package_test_is_broken(PackageKey::feature(f)); + let test_is_broken = |f: &str| { + let getparents = |f: &str| features_with_deps.get(f).map(|(d, _)| d); + match get_transitive_val(&getparents, &test_is_marked_broken, f) { + Err((k, vv)) => debcargo_bail!( + "{} {}: {}: {:?}", + "error trying to recursively determine test_is_broken for", + k, + "dependencies have inconsistent config values", + vv + ), + Ok(v) => Ok(v.unwrap_or(false)), + } + }; + + let build_deps = { + let build_deps = ["debhelper (>= 12)", "dh-cargo (>= 25)"] + .iter() + .map(|x| x.to_string()); + // note: please keep this in sync with build_order::dep_features + let (default_features, default_deps) = transitive_deps(&features_with_deps, "default"); + //debcargo_info!("default_features: {:?}", default_features); + //debcargo_info!("default_deps: {:?}", deb_deps(config, &default_deps)?); + let extra_override_deps = package_field_for_feature( + &|x| config.package_depends(x), + PackageKey::feature("default"), + &default_features, + ); + let build_deps_extra = ["cargo:native", "rustc:native", "libstd-rust-dev"] + .iter() + .map(|s| s.to_string()) + .chain(deb_deps(config, &default_deps)?) + .chain(extra_override_deps); + if !bins.is_empty() { + build_deps.chain(build_deps_extra).collect() + } else { + assert!(lib); + build_deps + .chain(build_deps_extra.map(|d| deb_dep_add_nocheck(&d))) + .collect() + } + }; + let mut source = Source::new( + base_pkgname, + name_suffix, + crate_name, + if let Some(ref home) = meta.homepage { + home + } else { + "" + }, + lib, + maintainer.to_string(), + uploaders.iter().map(|s| s.to_string()).collect(), + build_deps, + if requires_root.is_some() { + requires_root.as_ref().unwrap().to_string() + } else { + "no".to_string() + }, + )?; + + // If source overrides are present update related parts. + source.apply_overrides(config); + + let mut control = io::BufWriter::new(file("control")?); + write!(control, "{}", source)?; + + // Summary and description generated from Cargo.toml + let (crate_summary, crate_description) = crate_info.get_summary_description(); + let summary_prefix = crate_summary.unwrap_or(format!("Rust crate \"{}\"", crate_name)); + let description_prefix = { + let tmp = crate_description.unwrap_or_else(|| "".to_string()); + if tmp.is_empty() { + tmp + } else { + format!("{}\n.\n", tmp) + } + }; + + if lib { + // debian/tests/control + let all_features_test_broken = Some(&"@") + .into_iter() + .chain(features_with_deps.keys()) + .any(|f| test_is_marked_broken(f).unwrap_or(false)); + let all_features_test_depends = Some(&"@") + .into_iter() + .chain(features_with_deps.keys()) + .map(|f| { + config + .package_test_depends(PackageKey::feature(f)) + .into_iter() + .flatten() + }) + .flatten() + .map(|s| s.to_string()) + .chain(dev_depends.clone()) + .collect::>(); + let mut testctl = io::BufWriter::new(file("tests/control")?); + write!( + testctl, + "{}", + PkgTest::new( + source.name(), + crate_name, + "@", + deb_upstream_version, + vec!["--all-features"], + &all_features_test_depends, + if all_features_test_broken { + vec!["flaky"] + } else { + vec![] + }, + )? + )?; + + // begin transforming dependencies + let working_features_with_deps = features_with_deps.clone(); + let working_features_with_deps = { + let mut working_features_with_deps = working_features_with_deps; + // Detect corner case with feature naming regarding _ vs -. + // Debian does not support _ in package names. Cargo automatically + // converts - in crate names to _, but features (including optional + // dependencies) can have both _ and -. + let potential_corner_case = working_features_with_deps + .keys() + .filter(|x| base_deb_name(x).as_str() != **x) + .cloned() + .collect::>(); + for f in potential_corner_case { + let f_ = base_deb_name(f); + if let Some((df1, dd1)) = working_features_with_deps.remove(f_.as_str()) { + // merge dependencies of f_ and f + working_features_with_deps + .entry(f) + .and_modify(|(df0, dd0)| { + let mut df = BTreeSet::from_iter(df0.drain(..)); + df.extend(df1); + df.remove(f_.as_str()); + df.remove(f); + let mut dd: HashSet = + HashSet::from_iter(dd0.drain(..)); + dd.extend(dd1); + df0.extend(df); + dd0.extend(dd); + }); + // go through other feature deps and change f_ to f + for (_, (df, _)) in working_features_with_deps.iter_mut() { + for feat in df.iter_mut() { + if *feat == f_.as_str() { + *feat = f; + } + } + } + // check we didn't create a cycle in features + let dep_feats = traverse_depth( + &|k: &&'static str| working_features_with_deps.get(k).map(|x| &x.0), + f, + ); + if dep_feats.contains(f) { + log::debug!("transitive deps of feature {}: {:?}", f, dep_feats); + debcargo_bail!( + "Tried to merge features {} and {} as they are not representable separately\n\ + in Debian, but this resulted in a feature cycle. You need to manually patch the package.", f, f_); + } else { + debcargo_warn!( + "Merged features {} and {} as they are not representable separately in Debian.\n\ + We checked that this does not break the package in an obvious way (feature cycle), however\n\ + if there is a more sophisticated breakage, you'll have to manually patch those \ + features instead.", f, f_); + } + } + } + working_features_with_deps + }; + log::trace!( + "working_features_with_deps: {:?}", + working_features_with_deps + .iter() + .map(|(&f, &(ref ff, ref dd))| { + (f, (ff, dd.iter().map(show_dep).collect::>())) + }) + .collect::>() + ); + let (mut provides, reduced_features_with_deps) = if config.collapse_features { + debcargo_warn!( + "You are using the collapse_features work-around, which makes the resulting" + ); + debcargo_warn!( + "package uninstallable when (now or in the future) your crate dependencies" + ); + debcargo_warn!( + "contain cyclic dependencies on the crate-level; this is because cargo only" + ); + debcargo_warn!("enforces acyclicity of dependencies on the per-feature level."); + debcargo_warn!(""); + debcargo_warn!( + "By switching on collapse_features, you are telling debcargo to generate Debian" + ); + debcargo_warn!( + "binary package on a per-crate-level basis and not a per-feature-level, meaning" + ); + debcargo_warn!( + "that there is the chance of generating a dependency cycle on the Debian binary" + ); + debcargo_warn!("package level, which APT by default refuses to install."); + debcargo_warn!(""); + debcargo_warn!( + "You should not be doing this just because \"somebody told you so\"; you should" + ); + debcargo_warn!( + "understand the situation and be prepared to deal with future technical debt" + ); + debcargo_warn!("when the aforementioned cycles arise."); + debcargo_warn!(""); + debcargo_warn!( + "Note that a long-term solution has been discussed with the FTP team and is in" + ); + debcargo_warn!( + "progress - namely to move Debian rust packages into a separate section of the" + ); + debcargo_warn!( + "archive, which will then have the stricter current NEW rules lifted, and then" + ); + debcargo_warn!("the collapse_features work around would no longer be necessary."); + debcargo_warn!(""); + debcargo_warn!("A basic example of the above would be:"); + debcargo_warn!(""); + debcargo_warn!("- crate A with feature AX depends on crate B with feature BY"); + debcargo_warn!("- crate B with feature BX depends on crate A with feature AY"); + debcargo_warn!(""); + debcargo_warn!( + "This is a perfectly valid situation in the rust+cargo ecosystem. Notice that" + ); + debcargo_warn!( + "there is no dependency cycle on the per-feature level, and this is enforced by" + ); + debcargo_warn!( + "cargo; but if collapse_features is used then package A+AX+AY would cyclicly" + ); + debcargo_warn!("depend on package B+BX+BY."); + collapse_features(working_features_with_deps) + } else { + reduce_provides(working_features_with_deps) + }; + log::trace!( + "reduced_features_with_deps: {:?}", + reduced_features_with_deps + .iter() + .map(|(&f, &(ref ff, ref dd))| { + (f, (ff, dd.iter().map(show_dep).collect::>())) + }) + .collect::>() + ); + // end transforming dependencies + + log::trace!("provides: {:?}", provides); + let mut recommends = vec![]; + let mut suggests = vec![]; + for (&feature, features) in provides.iter() { + if feature.is_empty() { + continue; + } else if feature == "default" || features.contains(&"default") { + recommends.push(feature); + } else { + suggests.push(feature); + } + } + + for (feature, (f_deps, o_deps)) in reduced_features_with_deps.into_iter() { + let pk = PackageKey::feature(feature); + let f_provides = provides.remove(feature).unwrap(); + let mut crate_features = f_provides.clone(); + crate_features.push(feature); + + let summary_suffix = if feature.is_empty() { + " - Rust source code".to_string() + } else { + match f_provides.len() { + 0 => format!(" - feature \"{}\"", feature), + _ => format!(" - feature \"{}\" and {} more", feature, f_provides.len()), + } + }; + let description_suffix = if feature.is_empty() { + format!( + "This package contains the source for the \ + Rust {} crate, packaged by debcargo for use \ + with cargo and dh-cargo.", + crate_name + ) + } else { + format!( + "This metapackage enables feature \"{}\" for the \ + Rust {} crate, by pulling in any additional \ + dependencies needed by that feature.{}", + feature, + crate_name, + match f_provides.len() { + 0 => "".to_string(), + 1 => format!( + "\n\nAdditionally, this package also provides the \ + \"{}\" feature.", + f_provides[0], + ), + _ => format!( + "\n\nAdditionally, this package also provides the \ + \"{}\", and \"{}\" features.", + f_provides[..f_provides.len() - 1].join("\", \""), + f_provides[f_provides.len() - 1], + ), + }, + ) + }; + let mut package = Package::new( + base_pkgname, + name_suffix, + crate_info.version(), + Description { + prefix: summary_prefix.clone(), + suffix: summary_suffix.clone(), + }, + Description { + prefix: description_prefix.clone(), + suffix: description_suffix.clone(), + }, + if feature.is_empty() { + None + } else { + Some(feature) + }, + f_deps, + deb_deps(config, &o_deps)?, + f_provides.clone(), + if feature.is_empty() { + recommends.clone() + } else { + vec![] + }, + if feature.is_empty() { + suggests.clone() + } else { + vec![] + }, + )?; + // If any overrides present for this package it will be taken care. + package.apply_overrides(config, pk, f_provides); + + match package.summary_check_len() { + Err(()) => writeln!( + control, + concat!( + "\n", + "# FIXME (packages.\"(name)\".section) debcargo ", + "auto-generated summary for {} is very long, consider overriding" + ), + package.name(), + )?, + Ok(()) => {} + }; + + write!(control, "\n{}", package)?; + + // Override pointless overzealous warnings from lintian + if !feature.is_empty() { + let mut overrides = + io::BufWriter::new(file(&format!("{}.lintian-overrides", package.name()))?); + write!( + overrides, + "{} binary: empty-rust-library-declares-provides *", + package.name() + )?; + } + + // Generate tests for all features in this package + for f in crate_features { + let (feature_deps, _) = transitive_deps(&features_with_deps, f); + + // args + let mut args = if f == "default" || feature_deps.contains(&"default") { + vec![] + } else { + vec!["--no-default-features"] + }; + // --features default sometimes fails, see + // https://github.com/rust-lang/cargo/issues/8164 + if !f.is_empty() && f != "default" { + args.push("--features"); + args.push(f); + } + + // deps + let test_depends = Some(f) + .into_iter() + .chain(feature_deps) + .map(|f| { + config + .package_test_depends(PackageKey::feature(f)) + .into_iter() + .flatten() + }) + .flatten() + .map(|s| s.to_string()) + .chain(dev_depends.clone()) + .collect::>(); + let pkgtest = PkgTest::new( + package.name(), + crate_name, + f, + deb_upstream_version, + args, + &test_depends, + if test_is_broken(f)? { + vec!["flaky"] + } else { + vec![] + }, + )?; + write!(testctl, "\n{}", pkgtest)?; + } + } + assert!(provides.is_empty()); + // reduced_features_with_deps consumed by into_iter, no longer usable + } + + if !bins.is_empty() { + // adding " - binaries" is a bit redundant for users, so just leave as-is + let summary_suffix = "".to_string(); + let description_suffix = format!( + "This package contains the following binaries built from the Rust crate\n\"{}\":\n - {}", + crate_name, + bins.join("\n - ") + ); + + let mut bin_pkg = Package::new_bin( + bin_name, + name_suffix, + // if not-a-lib then Source section is already FIXME + if !lib { + None + } else { + Some("FIXME-(packages.\"(name)\".section)") + }, + Description { + prefix: summary_prefix, + suffix: summary_suffix, + }, + Description { + prefix: description_prefix, + suffix: description_suffix, + }, + ); + + // Binary package overrides. + bin_pkg.apply_overrides(config, PackageKey::Bin, vec![]); + write!(control, "\n{}", bin_pkg)?; + } + + Ok((source, !dev_depends.is_empty(), test_is_broken("default")?)) +} + +fn collapse_features( + orig_features_with_deps: CrateDepInfo, +) -> (BTreeMap<&'static str, Vec<&'static str>>, CrateDepInfo) { + let (provides, deps) = orig_features_with_deps.iter().fold( + (Vec::new(), Vec::new()), + |(mut provides, mut deps), (f, (_, f_deps))| { + if f != &"" { + provides.push(*f); + } + deps.append(&mut f_deps.clone()); + (provides, deps) + }, + ); + + let mut collapsed_provides = BTreeMap::new(); + collapsed_provides.insert("", provides); + + let mut collapsed_features_with_deps = BTreeMap::new(); + collapsed_features_with_deps.insert("", (Vec::new(), deps)); + + (collapsed_provides, collapsed_features_with_deps) +} + +/// Calculate Provides: in an attempt to reduce the number of binaries. +/// +/// The algorithm is very simple and incomplete. e.g. it does not, yet +/// simplify things like: +/// f1 depends on f2, f3 +/// f2 depends on f4 +/// f3 depends on f4 +/// into +/// f4 provides f1, f2, f3 +fn reduce_provides( + mut features_with_deps: CrateDepInfo, +) -> (BTreeMap<&'static str, Vec<&'static str>>, CrateDepInfo) { + // If any features have duplicate dependencies, deduplicate them by + // making all of the subsequent ones depend on the first one. + let mut features_rev_deps = HashMap::new(); + for (&f, dep) in features_with_deps.iter() { + if !features_rev_deps.contains_key(dep) { + features_rev_deps.insert(dep.clone(), vec![]); + } + features_rev_deps.get_mut(dep).unwrap().push(f); + } + for (_, ff) in features_rev_deps.into_iter() { + let f0 = ff[0]; + for f in &ff[1..] { + features_with_deps.insert(f, (vec![f0], vec![])); + } + } + + // Calculate provides by following 0- or 1-length dependency lists. + let mut provides = BTreeMap::new(); + let mut provided = Vec::new(); + for (&f, (ref ff, ref dd)) in features_with_deps.iter() { + //debcargo_info!("provides considering: {:?}", &f); + if !dd.is_empty() { + continue; + } + assert!(!ff.is_empty() || f.is_empty()); + let k = if ff.len() == 1 { + // if A depends on a single feature B, then B provides A. + ff[0] + } else { + continue; + }; + //debcargo_info!("provides still considering: {:?}", &f); + if !provides.contains_key(k) { + provides.insert(k, vec![]); + } + provides.get_mut(k).unwrap().push(f); + provided.push(f); + } + + //debcargo_info!("provides-internal: {:?}", &provides); + //debcargo_info!("provided-internal: {:?}", &provided); + for p in provided { + features_with_deps.remove(p); + } + + let provides = features_with_deps + .keys() + .map(|k| { + ( + *k, + traverse_depth(&|k: &&'static str| provides.get(k), k) + .into_iter() + .collect::>(), + ) + }) + .collect::>(); + + (provides, features_with_deps) +} + +fn changelog_or_new(tempdir: &Path) -> Result<(fs::File, String)> { + let mut changelog = fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(tempdir.join("changelog"))?; + let mut changelog_data = String::new(); + changelog.read_to_string(&mut changelog_data)?; + Ok((changelog, changelog_data)) +} + +fn changelog_first_last(tempdir: &Path) -> Result<(i32, i32)> { + let mut changelog = fs::File::open(tempdir.join("changelog"))?; + let mut changelog_data = String::new(); + changelog.read_to_string(&mut changelog_data)?; + let mut last = None; + let mut first = None; + for x in ChangelogIterator::from(&changelog_data) { + let e = ChangelogEntry::from_str(x)?; + if None == last { + last = Some(e.date.year()); + } + first = Some(e.date.year()); + } + if None == last { + Err(format_err!("changelog had no entries")) + } else { + Ok((first.unwrap(), last.unwrap())) + } +} diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..7fd1511 --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,55 @@ +use anyhow; + +pub type Result = ::std::result::Result; +pub use anyhow::format_err; + +#[macro_export] +macro_rules! debcargo_info { + ($e:expr) => { + { + use ansi_term::Colour::Green; + eprintln!("{}", Green.paint($e)); + } + }; + + ($fmt:expr, $( $arg:tt)+) => { + { + use ansi_term::Colour::Green; + let print_string = format!($fmt, $($arg)+); + eprintln!("{}", Green.paint(print_string)); + } + }; +} + +#[macro_export] +macro_rules! debcargo_warn { + ($e:expr) => { + { + use ansi_term::Colour::RGB; + eprintln!("{}", RGB(255,165,0).bold().paint($e)); + } + }; + + ($fmt:expr, $( $arg:tt)+) => { + { + use ansi_term::Colour::RGB; + let print_string = RGB(255,165,0).bold().paint(format!($fmt, $($arg)+)); + eprintln!("{}", print_string); + } + }; + +} + +#[macro_export] +macro_rules! debcargo_bail { + ($e:expr) => {{ + return Err(::anyhow::format_err!("{}", $e)); + }}; + + ($fmt:expr, $( $arg:tt)+) => { + { + let error_string = format!($fmt, $($arg)+); + return Err(::anyhow::format_err!("{}", error_string)); + } + }; +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..7ba1af6 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,9 @@ +#[macro_use] +pub mod errors; +pub mod config; +pub mod crates; +pub mod debian; +mod util; + +pub mod build_order; +pub mod package; diff --git a/src/package.rs b/src/package.rs new file mode 100644 index 0000000..1f60c14 --- /dev/null +++ b/src/package.rs @@ -0,0 +1,251 @@ +use std::path::PathBuf; + +use anyhow::Context; + +use clap::{crate_version, Parser}; + +use crate::config::Config; +use crate::crates::CrateInfo; +use crate::debian::{self, DebInfo}; +use crate::errors::Result; +use crate::util; + +pub struct PackageProcess { + // below state is filled in during init + pub crate_info: CrateInfo, + pub deb_info: DebInfo, + pub config_path: Option, + pub config: Config, + // below state is filled in during the process + /// Output directory as specified by the user. + pub output_dir: Option, + pub source_modified: Option, + /// Tempdir that contains a working copy of the eventual output. + pub temp_output_dir: Option, + pub orig_tarball: Option, +} + +#[derive(Debug, Clone, Parser)] +pub struct PackageInitArgs { + /// Name of the crate to package. + pub crate_name: String, + /// Version of the crate to package; may contain dependency operators. + /// If empty string or omitted, resolves to the latest version. + pub version: Option, + /// TOML file providing package-specific options. + #[clap(long)] + pub config: Option, +} + +#[derive(Debug, Clone, Parser)] +pub struct PackageExtractArgs { + /// Output directory for the package. The orig tarball is named according + /// to Debian conventions in the parent directory of this directory. + #[clap(long)] + pub directory: Option, +} + +#[derive(Debug, Clone, Parser)] +pub struct PackageExecuteArgs { + /// Assume the changelog is already bumped, and leave it alone. + #[clap(long)] + pub changelog_ready: bool, + /// Guess extra values for d/copyright. Might be slow. + #[clap(long)] + pub copyright_guess_harder: bool, + /// Don't write back hint files or d/changelog to the source overlay directory. + #[clap(long)] + pub no_overlay_write_back: bool, +} + +impl PackageProcess { + /// More fine-grained access. For normal usage see `Self::init` instead. + pub fn new( + mut crate_info: CrateInfo, + config_path: Option, + config: Config, + ) -> Result { + crate_info.set_includes_excludes(config.orig_tar_excludes(), config.orig_tar_whitelist()); + let deb_info = DebInfo::new(&crate_info, crate_version!(), config.semver_suffix); + + Ok(Self { + crate_info, + deb_info, + config_path, + config, + output_dir: None, + source_modified: None, + temp_output_dir: None, + orig_tarball: None, + }) + } + + pub fn init(init_args: PackageInitArgs) -> Result { + let crate_name = &init_args.crate_name; + let version = init_args.version.as_deref(); + let config = init_args.config; + + let (config_path, config) = match config { + Some(path) => { + let config = Config::parse(&path).context("failed to parse debcargo.toml")?; + (Some(path), config) + } + None => (None, Config::default()), + }; + + let crate_path = config.crate_src_path(config_path.as_deref()); + let crate_info = match crate_path { + Some(p) => CrateInfo::new_with_local_crate(crate_name, version, &p)?, + None => CrateInfo::new(crate_name, version)?, + }; + + Self::new(crate_info, config_path, config) + } + + pub fn extract(&mut self, extract: PackageExtractArgs) -> Result<()> { + assert!(self.output_dir.is_none()); + assert!(self.source_modified.is_none()); + let Self { + crate_info, + deb_info, + .. + } = self; + // vars read; begin stage + + let output_dir = extract + .directory + .unwrap_or_else(|| deb_info.package_source_dir().to_path_buf()); + + let source_modified = crate_info.extract_crate(&output_dir)?; + + // stage finished; set vars + self.output_dir = Some(output_dir); + self.source_modified = Some(source_modified); + Ok(()) + } + + pub fn apply_overrides(&mut self) -> Result<()> { + assert!(self.temp_output_dir.is_none()); + let Self { + crate_info, + config_path, + config, + output_dir, + .. + } = self; + let output_dir = output_dir.as_ref().unwrap(); + // vars read; begin stage + + let temp_output_dir = debian::apply_overlay_and_patches( + crate_info, + config_path.as_deref(), + config, + output_dir, + )?; + + // stage finished; set vars + self.temp_output_dir = Some(temp_output_dir); + Ok(()) + } + + pub fn prepare_orig_tarball(&mut self) -> Result<()> { + assert!(self.orig_tarball.is_none()); + let Self { + crate_info, + deb_info, + output_dir, + source_modified, + .. + } = self; + let output_dir = output_dir.as_ref().unwrap(); + let source_modified = source_modified.as_ref().unwrap(); + // vars read; begin stage + + let orig_tarball = output_dir + .parent() + .unwrap() + .join(deb_info.orig_tarball_path()); + debian::prepare_orig_tarball(crate_info, &orig_tarball, *source_modified, output_dir)?; + + // stage finished; set vars + self.orig_tarball = Some(orig_tarball); + Ok(()) + } + + pub fn prepare_debian_folder(&mut self, args: PackageExecuteArgs) -> Result<()> { + let Self { + crate_info, + deb_info, + config_path, + config, + output_dir, + temp_output_dir, + .. + } = self; + let output_dir = output_dir.as_ref().unwrap(); + let temp_output_dir = temp_output_dir.as_ref().unwrap(); + // vars read; begin stage + + debian::prepare_debian_folder( + crate_info, + deb_info, + config_path.as_deref(), + config, + output_dir, + temp_output_dir, + args.changelog_ready, + args.copyright_guess_harder, + !args.no_overlay_write_back, + )?; + + // stage finished; set vars + Ok(()) + } + + pub fn post_package_checks(&self) -> Result<()> { + let Self { + config_path, + config, + output_dir, + orig_tarball, + .. + } = self; + let output_dir = output_dir.as_ref().unwrap(); + let orig_tarball = orig_tarball.as_ref().unwrap(); + + let curdir = std::env::current_dir()?; + debcargo_info!( + concat!("Package Source: {}\n", "Original Tarball for package: {}\n"), + util::rel_p(output_dir, &curdir), + util::rel_p(orig_tarball, &curdir) + ); + let fixmes = util::lookup_fixmes(output_dir.join("debian").as_path())?; + if !fixmes.is_empty() { + debcargo_warn!("FIXME found in the following files."); + for f in fixmes { + if util::hint_file_for(&f).is_some() { + debcargo_warn!("\t(•) {}", util::rel_p(&f, &curdir)); + } else { + debcargo_warn!("\t • {}", util::rel_p(&f, &curdir)); + } + } + debcargo_warn!(""); + debcargo_warn!("To fix, try combinations of the following: "); + match config_path.as_deref() { + None => debcargo_warn!("\t • Write a config file and use it with --config"), + Some(c) => { + debcargo_warn!("\t • Add or edit overrides in your config file:"); + debcargo_warn!("\t {}", util::rel_p(c, &curdir)); + } + }; + match config.overlay_dir(config_path.as_deref()) { + None => debcargo_warn!("\t • Create an overlay directory and add it to your config file with overlay = \"/path/to/overlay\""), + Some(p) => { + debcargo_warn!("\t • Add or edit files in your overlay directory:"); + debcargo_warn!("\t {}", util::rel_p(&p, &curdir)); + } + } + } + Ok(()) + } +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..473b137 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,253 @@ +use std::collections::{BTreeMap, BTreeSet, VecDeque}; +use std::ffi::OsStr; +use std::fmt; +use std::fs; +use std::io::{BufRead, BufReader, Error}; +use std::iter::Iterator; +use std::os::unix::ffi::OsStrExt; +use std::os::unix::fs::symlink; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use itertools::Itertools; +use walkdir::WalkDir; + +pub const HINT_SUFFIX: &str = ".debcargo.hint"; + +pub fn hint_file_for(file: &Path) -> Option<&Path> { + let file = file.as_os_str().as_bytes(); + if file.len() >= HINT_SUFFIX.len() + && &file[file.len() - HINT_SUFFIX.len()..] == HINT_SUFFIX.as_bytes() + { + Some(Path::new(OsStr::from_bytes( + &file[..file.len() - HINT_SUFFIX.len()], + ))) + } else { + None + } +} + +pub fn lookup_fixmes(srcdir: &Path) -> Result, Error> { + let mut fixmes = BTreeSet::new(); + for entry in WalkDir::new(srcdir) { + let entry = entry?; + if entry.file_type().is_file() { + let file = fs::File::open(entry.path())?; + let reader = BufReader::new(file); + // If we find one FIXME we break the loop and check next file. Idea + // is only to find files with FIXME strings in it. + for line in reader.lines().flatten() { + if line.contains("FIXME") { + fixmes.insert(entry.path().to_path_buf()); + break; + } + } + } + } + + // ignore hint files whose non-hint partners exists and don't have a FIXME + let fixmes = fixmes + .iter() + .filter(|f| match hint_file_for(f) { + Some(ff) => fixmes.contains(ff) || !ff.exists(), + None => true, + }) + .cloned() + .collect::>(); + Ok(fixmes) +} + +pub fn rel_p<'a>(path: &'a Path, base: &'a Path) -> &'a str { + path.strip_prefix(base).unwrap_or(path).to_str().unwrap() +} + +pub fn copy_tree(oldtree: &Path, newtree: &Path) -> Result<(), Error> { + for entry in WalkDir::new(oldtree) { + let entry = entry?; + if entry.depth() == 0 { + continue; + } + let oldpath = entry.path(); + let newpath = newtree.join(oldpath.strip_prefix(&oldtree).unwrap()); + let ftype = entry.file_type(); + if ftype.is_dir() { + fs::create_dir(newpath)?; + } else if ftype.is_file() { + fs::copy(oldpath, newpath)?; + } else if ftype.is_symlink() { + symlink(fs::read_link(oldpath)?, newpath)?; + } + } + Ok(()) +} + +pub fn show_vec_with<'a, T, F>(it: impl IntoIterator, f: F) -> String +where + T: 'a, + F: FnMut(&T) -> String, +{ + Itertools::intersperse(it.into_iter().map(f), ", ".to_string()).collect::() +} + +pub fn show_vec<'a, T>(it: impl IntoIterator) -> String +where + T: fmt::Display + 'a, +{ + show_vec_with(it, std::string::ToString::to_string) +} + +pub fn expect_success(cmd: &mut Command, err: &str) { + match cmd.status() { + Ok(status) => { + if !status.success() { + panic!("{}", err); + } + } + Err(e) => { + panic!("{}\n{}", err, e); + } + } +} + +pub(crate) fn traverse_depth<'a, V, F>(succ: &'a F, key: V) -> BTreeSet +where + V: Ord + Copy + 'a, + F: Fn(&V) -> Option<&'a Vec>, +{ + let mut remain = VecDeque::from_iter([key]); + let mut seen = BTreeSet::new(); + while let Some(v) = remain.pop_front() { + for v_ in succ(&v).into_iter().flatten() { + if !seen.contains(v_) { + seen.insert(*v_); + remain.push_back(*v_); + } + } + } + seen +} + +/// Get a value that might be set at a key or any of its ancestor keys, +/// whichever is closest. Error if there are conflicting definitions. +#[allow(clippy::type_complexity)] +pub(crate) fn get_transitive_val< + 'a, + P: Fn(K) -> Option<&'a Vec>, + F: Fn(K) -> Option, + K: 'a + Ord + Copy, + V: Eq + Ord, +>( + getparents: &'a P, + f: &F, + key: K, +) -> Result, (K, Vec<(K, V)>)> { + let here = f(key); + if here.is_some() { + // value overrides anything from parents + Ok(here) + } else { + let mut candidates = Vec::new(); + for par in getparents(key).into_iter().flatten() { + if let Some(v) = get_transitive_val(getparents, f, *par)? { + candidates.push((*par, v)) + } + } + if candidates.is_empty() { + Ok(None) // here is None + } else { + let mut values = candidates.iter().map(|(_, v)| v).collect::>(); + values.sort(); + values.dedup(); + if values.len() == 1 { + Ok(candidates.pop().map(|(_, v)| v)) + } else { + Err((key, candidates)) // handle conflict + } + } + } +} + +pub fn graph_from_succ( + seed: impl IntoIterator, + succ: &mut FV, + log: &mut FL, +) -> Result>, E> +where + V: Ord + Clone, + FV: FnMut(&V) -> Result<(Vec, Vec), E>, + FL: FnMut(&VecDeque, &BTreeMap>) -> Result<(), E>, +{ + let mut seen = BTreeSet::from_iter(seed); + let mut graph = BTreeMap::new(); + let mut remain = VecDeque::from_iter(seen.iter().cloned()); + while let Some(v) = remain.pop_front() { + log(&remain, &graph)?; + let (hard, soft) = succ(&v)?; + for v_ in hard.iter().chain(soft.iter()) { + if !seen.contains(v_) { + seen.insert(v_.clone()); + remain.push_back(v_.clone()); + } + } + graph.insert(v, BTreeSet::from_iter(hard)); + } + Ok(graph) +} + +pub fn succ_proj(succ: &BTreeMap>, proj: F) -> BTreeMap> +where + F: Fn(&S) -> T, + S: Ord, + T: Ord + Clone, +{ + let mut succ_proj: BTreeMap> = BTreeMap::new(); + for (s, ss) in succ { + let e = succ_proj.entry(proj(s)).or_default(); + for s_ in ss { + e.insert(proj(s_)); + } + } + succ_proj +} + +pub fn succ_to_pred(succ: &BTreeMap>) -> BTreeMap> +where + V: Ord + Clone, +{ + let mut pred: BTreeMap> = BTreeMap::new(); + for (v, vv) in succ { + for v_ in vv { + pred.entry(v_.clone()).or_default().insert(v.clone()); + } + } + pred +} + +pub fn topo_sort( + seed: impl IntoIterator, + succ: BTreeMap>, + mut pred: BTreeMap>, +) -> Result, BTreeMap>> +where + V: Ord + Clone, +{ + let empty = BTreeSet::new(); + let mut remain = VecDeque::from_iter(seed); + let mut sort = Vec::new(); + while let Some(v) = remain.pop_front() { + sort.push(v.clone()); + for v_ in succ.get(&v).unwrap_or(&empty) { + let par = pred.entry(v_.clone()).or_default(); + par.remove(&v); + if par.is_empty() { + remain.push_back(v_.clone()); + } + } + } + pred.retain(|_, v| !v.is_empty()); + if !pred.is_empty() { + Err(pred) + } else { + Ok(sort) + } +} diff --git a/tests/clap_override.toml b/tests/clap_override.toml new file mode 100644 index 0000000..8595e84 --- /dev/null +++ b/tests/clap_override.toml @@ -0,0 +1,17 @@ +uploaders = ["Sylvestre Ledru ", "Ximin Luo " ] + +[source] +policy = "4.0.0" +homepage = "https://clap.rs" + +[packages.lib] +summary = "Simple, efficient and full featured Command line argument parser - source" +description = """ +clap is used to parse and validate string of command line arguments provided by +user at runtime. It provides help and version flags by default and additionally +provide help subcommands in addition to traditional flags. +This package provides clap with following default features. + * suggestions: provides did you mean suggestions on typos + * color: turns on colored error messages. + * wrap_help: Wrap the help at actual terminal width when available. +""" diff --git a/tests/config_tests.rs b/tests/config_tests.rs new file mode 100644 index 0000000..1825f0e --- /dev/null +++ b/tests/config_tests.rs @@ -0,0 +1,93 @@ +extern crate debcargo; + +use debcargo::config::{Config, PackageKey}; +use std::path::Path; + +#[test] +fn source_package_override() { + let filepath = Path::new("tests/clap_override.toml"); + + let config = Config::parse(filepath); + assert!(config.is_ok()); + + let config = config.unwrap(); + + assert!(config.source.is_some()); + assert!(config.packages.is_some()); + + let policy = config.policy_version(); + assert!(policy.is_some()); + assert_eq!(policy.unwrap(), "4.0.0"); + + let homepage = config.homepage(); + assert!(homepage.is_some()); + assert_eq!(homepage.unwrap(), "https://clap.rs"); + + assert!(config.section().is_none()); + assert!(config.build_depends().is_none()); + + let filepath = Path::new("tests/debcargo_override.toml"); + let config = Config::parse(filepath); + assert!(config.is_ok()); + + let config = config.unwrap(); + + assert!(config.source.is_some()); + + let section = config.section(); + assert!(section.is_some()); + assert_eq!(section.unwrap(), "rust"); + + assert!(config.packages.is_some()); + let sd = config.package_summary(PackageKey::Bin); + assert!(sd.is_some()); + + if let Some(s) = sd { + assert_eq!(s, "Tool to create Debian package from Rust crate"); + } + + let sd = config.package_description(PackageKey::Bin); + assert!(sd.is_some()); + if let Some(d) = sd { + assert_eq!( + d, + "\ +This package provides debcargo a tool to create Debian source package from \ + Rust +crate. The package created by this tool is as per the packaging policy \ + set by +Debian Rust team. +" + ); + } +} + +#[test] +fn sd_top_level() { + let filepath = Path::new("tests/debcargo_override_top_level.toml"); + let config = Config::parse(filepath); + assert!(config.is_ok()); + + let config = config.unwrap(); + + assert!(config.source.is_some()); + + let section = config.section(); + assert!(section.is_some()); + assert_eq!(section.unwrap(), "rust"); + + assert_eq!( + config.summary.unwrap(), + "Tool to create Debian package from Rust crate" + ); + assert_eq!( + config.description.unwrap(), + "\ +This package provides debcargo a tool to create Debian source package from \ + Rust +crate. The package created by this tool is as per the packaging policy \ + set by +Debian Rust team. +" + ); +} diff --git a/tests/configs/aesni-0.10/debian/debcargo.toml b/tests/configs/aesni-0.10/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/aesni-0.10/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/aesni-0.10/debian/rules b/tests/configs/aesni-0.10/debian/rules new file mode 100644 index 0000000..083418e --- /dev/null +++ b/tests/configs/aesni-0.10/debian/rules @@ -0,0 +1,9 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# crate requires builder to set custom RUSTFLAGS +# we can't do that in Debian due to architecture baselines +override_dh_auto_test: + dh_auto_test || true diff --git a/tests/configs/aesni/debian/debcargo.toml b/tests/configs/aesni/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/aesni/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/aesni/debian/rules b/tests/configs/aesni/debian/rules new file mode 100644 index 0000000..083418e --- /dev/null +++ b/tests/configs/aesni/debian/rules @@ -0,0 +1,9 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# crate requires builder to set custom RUSTFLAGS +# we can't do that in Debian due to architecture baselines +override_dh_auto_test: + dh_auto_test || true diff --git a/tests/configs/afl-0.10/debian/debcargo.toml b/tests/configs/afl-0.10/debian/debcargo.toml new file mode 100644 index 0000000..c30d237 --- /dev/null +++ b/tests/configs/afl-0.10/debian/debcargo.toml @@ -0,0 +1,8 @@ +semver_suffix = true +overlay = "." + +[source] +build_depends = ["llvm", "lld", "clang"] + +[packages.lib] +depends = ["libclang-dev", "llvm", "lld", "clang"] diff --git a/tests/configs/afl-0.10/debian/rules b/tests/configs/afl-0.10/debian/rules new file mode 100644 index 0000000..5f52057 --- /dev/null +++ b/tests/configs/afl-0.10/debian/rules @@ -0,0 +1,15 @@ +#!/usr/bin/make -f +%: + dh $@ --buildsystem cargo +export RUSTC_BOOTSTRAP := 1 +export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH) + +# needed by build script +export XDG_DATA_HOME := $(CURDIR)/debian/xdg-data-home +export CXXFLAGS := -Wno-unused-command-line-argument +export CFLAGS := -Wno-unused-command-line-argument + +override_dh_strip_nondeterminism: + # dh_strip_nondeterminism can't parse these files + rm -rf $(CURDIR)/debian/libruzt-afl-*-dev/usr/share/cargo/registry/afl-*/AFLplusplus/docs/vuln_samples + dh_strip_nondeterminism diff --git a/tests/configs/aom-sys-0.2/debian/debcargo.toml b/tests/configs/aom-sys-0.2/debian/debcargo.toml new file mode 100644 index 0000000..7ba4c47 --- /dev/null +++ b/tests/configs/aom-sys-0.2/debian/debcargo.toml @@ -0,0 +1,4 @@ +semver_suffix = true + +[packages.lib] +depends = ["libaom-dev"] diff --git a/tests/configs/ascii-0.8/debian/debcargo.toml b/tests/configs/ascii-0.8/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/ascii-0.8/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/ascii-0.8/debian/patches/fix-old-dep.patch b/tests/configs/ascii-0.8/debian/patches/fix-old-dep.patch new file mode 100644 index 0000000..3aa27b5 --- /dev/null +++ b/tests/configs/ascii-0.8/debian/patches/fix-old-dep.patch @@ -0,0 +1,20 @@ +Crate authors like to put dev-dependencies in dependencies due to +https://github.com/rust-lang/cargo/issues/1596 + +Then due to lack of maintenance this pollutes the dependency set with old +crates that no longer compile. + +Here, quickcheck 0.4 eventually depends on simd which no longer compiles. + +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -22,9 +22,6 @@ + [[test]] + name = "tests" + path = "tests.rs" +-[dependencies.quickcheck] +-version = "0.4.1" +-optional = true + + [features] + default = ["std"] diff --git a/tests/configs/ascii-0.8/debian/patches/series b/tests/configs/ascii-0.8/debian/patches/series new file mode 100644 index 0000000..f385166 --- /dev/null +++ b/tests/configs/ascii-0.8/debian/patches/series @@ -0,0 +1 @@ +fix-old-dep.patch diff --git a/tests/configs/broken-dh-strip/debian/debcargo.toml b/tests/configs/broken-dh-strip/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/broken-dh-strip/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/broken-dh-strip/debian/rules b/tests/configs/broken-dh-strip/debian/rules new file mode 100755 index 0000000..6c28854 --- /dev/null +++ b/tests/configs/broken-dh-strip/debian/rules @@ -0,0 +1,7 @@ +#!/usr/bin/make -f +%: + dh $@ --buildsystem cargo +export RUSTC_BOOTSTRAP := 1 +export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH) + +override_dh_strip: diff --git a/tests/configs/broken-lto/debian/debcargo.toml b/tests/configs/broken-lto/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/broken-lto/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/broken-lto/debian/rules b/tests/configs/broken-lto/debian/rules new file mode 100644 index 0000000..be9afde --- /dev/null +++ b/tests/configs/broken-lto/debian/rules @@ -0,0 +1,9 @@ +#!/usr/bin/make -f +%: + dh $@ --buildsystem cargo +export RUSTC_BOOTSTRAP := 1 +export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH) + +override_dh_dwz: + # Don't do anything. fails because of the + # https://github.com/rust-lang/rust/issues/66118 diff --git a/tests/configs/broken-upstream/debian/debcargo.toml b/tests/configs/broken-upstream/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/broken-upstream/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/broken-upstream/debian/rules b/tests/configs/broken-upstream/debian/rules new file mode 100644 index 0000000..bfcc84f --- /dev/null +++ b/tests/configs/broken-upstream/debian/rules @@ -0,0 +1,28 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# code uses old rust feature that causes compiler error. +override_dh_auto_test: + dh_auto_test || true + +# This crate is actually broken; however there are several cases why this +# override is OK and necessary for Debian: +# +# 1. The other crate depends on this broken crate in an architecture-specific +# way i.e. with [target.$arch.*] in Cargo.toml +# +# cargo does not yet support omitting these dependencies on other arches [1] +# and so we are forced to include it also in debcargo [2] +# +# [1] https://github.com/rust-lang/cargo/issues/5896 +# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14 +# +# 2. The other crate depends on this broken crate only via one of its optional +# features that most users of that crate, don't need. You generally run into +# this situation when resolving via --resolve-type BinaryAllForDebianTesting +# +# If your situation does not fit into one of the above situations, you should +# re-consider the override. If your override is incorrect, the build failure +# will crop up again later, in the crate that depends on this broken crate. diff --git a/tests/configs/brotli-decompressor-2/debian/debcargo.toml b/tests/configs/brotli-decompressor-2/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/brotli-decompressor-2/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/brotli-decompressor-2/debian/rules b/tests/configs/brotli-decompressor-2/debian/rules new file mode 100644 index 0000000..be9afde --- /dev/null +++ b/tests/configs/brotli-decompressor-2/debian/rules @@ -0,0 +1,9 @@ +#!/usr/bin/make -f +%: + dh $@ --buildsystem cargo +export RUSTC_BOOTSTRAP := 1 +export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH) + +override_dh_dwz: + # Don't do anything. fails because of the + # https://github.com/rust-lang/rust/issues/66118 diff --git a/tests/configs/clang-sys-0.23/debian/debcargo.toml b/tests/configs/clang-sys-0.23/debian/debcargo.toml new file mode 100644 index 0000000..583dcce --- /dev/null +++ b/tests/configs/clang-sys-0.23/debian/debcargo.toml @@ -0,0 +1,8 @@ +semver_suffix = true +overlay = "." + +[source] +build_depends = ["llvm", "clang"] + +[packages.lib] +depends = ["libclang-dev", "lld", "clang"] diff --git a/tests/configs/clang-sys-0.26/debian/debcargo.toml b/tests/configs/clang-sys-0.26/debian/debcargo.toml new file mode 100644 index 0000000..583dcce --- /dev/null +++ b/tests/configs/clang-sys-0.26/debian/debcargo.toml @@ -0,0 +1,8 @@ +semver_suffix = true +overlay = "." + +[source] +build_depends = ["llvm", "clang"] + +[packages.lib] +depends = ["libclang-dev", "lld", "clang"] diff --git a/tests/configs/clang-sys-1/debian/debcargo.toml b/tests/configs/clang-sys-1/debian/debcargo.toml new file mode 100644 index 0000000..583dcce --- /dev/null +++ b/tests/configs/clang-sys-1/debian/debcargo.toml @@ -0,0 +1,8 @@ +semver_suffix = true +overlay = "." + +[source] +build_depends = ["llvm", "clang"] + +[packages.lib] +depends = ["libclang-dev", "lld", "clang"] diff --git a/tests/configs/clang-sys/debian/debcargo.toml b/tests/configs/clang-sys/debian/debcargo.toml new file mode 100644 index 0000000..583dcce --- /dev/null +++ b/tests/configs/clang-sys/debian/debcargo.toml @@ -0,0 +1,8 @@ +semver_suffix = true +overlay = "." + +[source] +build_depends = ["llvm", "clang"] + +[packages.lib] +depends = ["libclang-dev", "lld", "clang"] diff --git a/tests/configs/clippy-0.0/debian/debcargo.toml b/tests/configs/clippy-0.0/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/clippy-0.0/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/clippy-0.0/debian/rules b/tests/configs/clippy-0.0/debian/rules new file mode 100644 index 0000000..b2056b4 --- /dev/null +++ b/tests/configs/clippy-0.0/debian/rules @@ -0,0 +1,18 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# clippy upstream now errors on purpose with the message "clippy is no longer +# available on crates.io". Many old crates depend on clippy even though it is +# a dev-dependency because of [3] and there are far too many to patch. So just +# make the build of clippy "succeed"; it does not actually get used in the +# build of a reverse-dependency anyway. +# +# [1] https://github.com/rust-lang/cargo/issues/1596 + +override_dh_auto_test: + dh_auto_test || true + +override_dh_auto_install: + dh_auto_install || true diff --git a/tests/configs/cmake-0.1/debian/debcargo.toml b/tests/configs/cmake-0.1/debian/debcargo.toml new file mode 100644 index 0000000..79ca598 --- /dev/null +++ b/tests/configs/cmake-0.1/debian/debcargo.toml @@ -0,0 +1,4 @@ +semver_suffix = true + +[packages.lib] +depends = ["cmake"] diff --git a/tests/configs/compiler-builtins-0.1/debian/debcargo.toml b/tests/configs/compiler-builtins-0.1/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/compiler-builtins-0.1/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/compiler-builtins-0.1/debian/rules b/tests/configs/compiler-builtins-0.1/debian/rules new file mode 100644 index 0000000..bfcc84f --- /dev/null +++ b/tests/configs/compiler-builtins-0.1/debian/rules @@ -0,0 +1,28 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# code uses old rust feature that causes compiler error. +override_dh_auto_test: + dh_auto_test || true + +# This crate is actually broken; however there are several cases why this +# override is OK and necessary for Debian: +# +# 1. The other crate depends on this broken crate in an architecture-specific +# way i.e. with [target.$arch.*] in Cargo.toml +# +# cargo does not yet support omitting these dependencies on other arches [1] +# and so we are forced to include it also in debcargo [2] +# +# [1] https://github.com/rust-lang/cargo/issues/5896 +# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14 +# +# 2. The other crate depends on this broken crate only via one of its optional +# features that most users of that crate, don't need. You generally run into +# this situation when resolving via --resolve-type BinaryAllForDebianTesting +# +# If your situation does not fit into one of the above situations, you should +# re-consider the override. If your override is incorrect, the build failure +# will crop up again later, in the crate that depends on this broken crate. diff --git a/tests/configs/compiler-error-0.1/debian/debcargo.toml b/tests/configs/compiler-error-0.1/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/compiler-error-0.1/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/compiler-error-0.1/debian/rules b/tests/configs/compiler-error-0.1/debian/rules new file mode 100644 index 0000000..bfcc84f --- /dev/null +++ b/tests/configs/compiler-error-0.1/debian/rules @@ -0,0 +1,28 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# code uses old rust feature that causes compiler error. +override_dh_auto_test: + dh_auto_test || true + +# This crate is actually broken; however there are several cases why this +# override is OK and necessary for Debian: +# +# 1. The other crate depends on this broken crate in an architecture-specific +# way i.e. with [target.$arch.*] in Cargo.toml +# +# cargo does not yet support omitting these dependencies on other arches [1] +# and so we are forced to include it also in debcargo [2] +# +# [1] https://github.com/rust-lang/cargo/issues/5896 +# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14 +# +# 2. The other crate depends on this broken crate only via one of its optional +# features that most users of that crate, don't need. You generally run into +# this situation when resolving via --resolve-type BinaryAllForDebianTesting +# +# If your situation does not fit into one of the above situations, you should +# re-consider the override. If your override is incorrect, the build failure +# will crop up again later, in the crate that depends on this broken crate. diff --git a/tests/configs/compiletest-rs-0.3/debian/debcargo.toml b/tests/configs/compiletest-rs-0.3/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/compiletest-rs-0.3/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/compiletest-rs-0.3/debian/rules b/tests/configs/compiletest-rs-0.3/debian/rules new file mode 100644 index 0000000..8ad6a55 --- /dev/null +++ b/tests/configs/compiletest-rs-0.3/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# crate errors on the default feature by design +override_dh_auto_test: + dh_auto_test || true diff --git a/tests/configs/core-arch-0.1/debian/debcargo.toml b/tests/configs/core-arch-0.1/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/core-arch-0.1/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/core-arch-0.1/debian/rules b/tests/configs/core-arch-0.1/debian/rules new file mode 100644 index 0000000..bfcc84f --- /dev/null +++ b/tests/configs/core-arch-0.1/debian/rules @@ -0,0 +1,28 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# code uses old rust feature that causes compiler error. +override_dh_auto_test: + dh_auto_test || true + +# This crate is actually broken; however there are several cases why this +# override is OK and necessary for Debian: +# +# 1. The other crate depends on this broken crate in an architecture-specific +# way i.e. with [target.$arch.*] in Cargo.toml +# +# cargo does not yet support omitting these dependencies on other arches [1] +# and so we are forced to include it also in debcargo [2] +# +# [1] https://github.com/rust-lang/cargo/issues/5896 +# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14 +# +# 2. The other crate depends on this broken crate only via one of its optional +# features that most users of that crate, don't need. You generally run into +# this situation when resolving via --resolve-type BinaryAllForDebianTesting +# +# If your situation does not fit into one of the above situations, you should +# re-consider the override. If your override is incorrect, the build failure +# will crop up again later, in the crate that depends on this broken crate. diff --git a/tests/configs/core-error-0.0/debian/debcargo.toml b/tests/configs/core-error-0.0/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/core-error-0.0/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/core-error-0.0/debian/rules b/tests/configs/core-error-0.0/debian/rules new file mode 100644 index 0000000..bfcc84f --- /dev/null +++ b/tests/configs/core-error-0.0/debian/rules @@ -0,0 +1,28 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# code uses old rust feature that causes compiler error. +override_dh_auto_test: + dh_auto_test || true + +# This crate is actually broken; however there are several cases why this +# override is OK and necessary for Debian: +# +# 1. The other crate depends on this broken crate in an architecture-specific +# way i.e. with [target.$arch.*] in Cargo.toml +# +# cargo does not yet support omitting these dependencies on other arches [1] +# and so we are forced to include it also in debcargo [2] +# +# [1] https://github.com/rust-lang/cargo/issues/5896 +# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14 +# +# 2. The other crate depends on this broken crate only via one of its optional +# features that most users of that crate, don't need. You generally run into +# this situation when resolving via --resolve-type BinaryAllForDebianTesting +# +# If your situation does not fit into one of the above situations, you should +# re-consider the override. If your override is incorrect, the build failure +# will crop up again later, in the crate that depends on this broken crate. diff --git a/tests/configs/core-foundation-0.9/debian/debcargo.toml b/tests/configs/core-foundation-0.9/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/core-foundation-0.9/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/core-foundation-0.9/debian/rules b/tests/configs/core-foundation-0.9/debian/rules new file mode 100644 index 0000000..e3dbf82 --- /dev/null +++ b/tests/configs/core-foundation-0.9/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# only supported on macos +override_dh_auto_test: + case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac diff --git a/tests/configs/core-graphics-0.22/debian/debcargo.toml b/tests/configs/core-graphics-0.22/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/core-graphics-0.22/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/core-graphics-0.22/debian/rules b/tests/configs/core-graphics-0.22/debian/rules new file mode 100644 index 0000000..e3dbf82 --- /dev/null +++ b/tests/configs/core-graphics-0.22/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# only supported on macos +override_dh_auto_test: + case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac diff --git a/tests/configs/core-graphics-types-0.1/debian/debcargo.toml b/tests/configs/core-graphics-types-0.1/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/core-graphics-types-0.1/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/core-graphics-types-0.1/debian/rules b/tests/configs/core-graphics-types-0.1/debian/rules new file mode 100644 index 0000000..e3dbf82 --- /dev/null +++ b/tests/configs/core-graphics-types-0.1/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# only supported on macos +override_dh_auto_test: + case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac diff --git a/tests/configs/core-text-19/debian/debcargo.toml b/tests/configs/core-text-19/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/core-text-19/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/core-text-19/debian/rules b/tests/configs/core-text-19/debian/rules new file mode 100644 index 0000000..e3dbf82 --- /dev/null +++ b/tests/configs/core-text-19/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# only supported on macos +override_dh_auto_test: + case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac diff --git a/tests/configs/cpp-demangle-0.3/debian/debcargo.toml b/tests/configs/cpp-demangle-0.3/debian/debcargo.toml new file mode 100644 index 0000000..7591b7f --- /dev/null +++ b/tests/configs/cpp-demangle-0.3/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +bin = false diff --git a/tests/configs/dav1d-sys-0.3/debian/debcargo.toml b/tests/configs/dav1d-sys-0.3/debian/debcargo.toml new file mode 100644 index 0000000..7611a91 --- /dev/null +++ b/tests/configs/dav1d-sys-0.3/debian/debcargo.toml @@ -0,0 +1,4 @@ +semver_suffix = true + +[packages.lib] +depends = ["libdav1d-dev"] diff --git a/tests/configs/diesel-1/debian/debcargo.toml b/tests/configs/diesel-1/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/diesel-1/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/diesel-1/debian/patches/fix-old-dep.patch b/tests/configs/diesel-1/debian/patches/fix-old-dep.patch new file mode 100644 index 0000000..79a0a39 --- /dev/null +++ b/tests/configs/diesel-1/debian/patches/fix-old-dep.patch @@ -0,0 +1,21 @@ +Crate authors like to put dev-dependencies in dependencies due to +https://github.com/rust-lang/cargo/issues/1596 + +Then due to lack of maintenance this pollutes the dependency set with old +crates that no longer compile. + +Here, quickcheck 0.4 eventually depends on simd which no longer compiles. + +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -76,10 +76,6 @@ + version = ">=0.3.0, <0.5.0" + optional = true + +-[dependencies.quickcheck] +-version = "0.4" +-optional = true +- + [dependencies.r2d2] + version = ">= 0.8, < 0.9" + optional = true diff --git a/tests/configs/diesel-1/debian/patches/series b/tests/configs/diesel-1/debian/patches/series new file mode 100644 index 0000000..f385166 --- /dev/null +++ b/tests/configs/diesel-1/debian/patches/series @@ -0,0 +1 @@ +fix-old-dep.patch diff --git a/tests/configs/dwrote-0.11/debian/debcargo.toml b/tests/configs/dwrote-0.11/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/dwrote-0.11/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/dwrote-0.11/debian/rules b/tests/configs/dwrote-0.11/debian/rules new file mode 100644 index 0000000..b92cbbc --- /dev/null +++ b/tests/configs/dwrote-0.11/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# only supported on windows +override_dh_auto_test: + case $(DEB_HOST_RUST_TYPE) in *-pc-windows-gnu) dh_auto_test;; *) true;; esac diff --git a/tests/configs/fd-find-8/debian/debcargo.toml b/tests/configs/fd-find-8/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/fd-find-8/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/fd-find-8/debian/rules b/tests/configs/fd-find-8/debian/rules new file mode 100644 index 0000000..be9afde --- /dev/null +++ b/tests/configs/fd-find-8/debian/rules @@ -0,0 +1,9 @@ +#!/usr/bin/make -f +%: + dh $@ --buildsystem cargo +export RUSTC_BOOTSTRAP := 1 +export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH) + +override_dh_dwz: + # Don't do anything. fails because of the + # https://github.com/rust-lang/rust/issues/66118 diff --git a/tests/configs/fsevent-0.4/debian/debcargo.toml b/tests/configs/fsevent-0.4/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/fsevent-0.4/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/fsevent-0.4/debian/rules b/tests/configs/fsevent-0.4/debian/rules new file mode 100644 index 0000000..e3dbf82 --- /dev/null +++ b/tests/configs/fsevent-0.4/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# only supported on macos +override_dh_auto_test: + case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac diff --git a/tests/configs/fsevent-sys-2/debian/debcargo.toml b/tests/configs/fsevent-sys-2/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/fsevent-sys-2/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/fsevent-sys-2/debian/rules b/tests/configs/fsevent-sys-2/debian/rules new file mode 100644 index 0000000..e3dbf82 --- /dev/null +++ b/tests/configs/fsevent-sys-2/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# only supported on macos +override_dh_auto_test: + case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac diff --git a/tests/configs/heapsize-plugin-0.1/debian/debcargo.toml b/tests/configs/heapsize-plugin-0.1/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/heapsize-plugin-0.1/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/heapsize-plugin-0.1/debian/rules b/tests/configs/heapsize-plugin-0.1/debian/rules new file mode 100644 index 0000000..bfcc84f --- /dev/null +++ b/tests/configs/heapsize-plugin-0.1/debian/rules @@ -0,0 +1,28 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# code uses old rust feature that causes compiler error. +override_dh_auto_test: + dh_auto_test || true + +# This crate is actually broken; however there are several cases why this +# override is OK and necessary for Debian: +# +# 1. The other crate depends on this broken crate in an architecture-specific +# way i.e. with [target.$arch.*] in Cargo.toml +# +# cargo does not yet support omitting these dependencies on other arches [1] +# and so we are forced to include it also in debcargo [2] +# +# [1] https://github.com/rust-lang/cargo/issues/5896 +# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14 +# +# 2. The other crate depends on this broken crate only via one of its optional +# features that most users of that crate, don't need. You generally run into +# this situation when resolving via --resolve-type BinaryAllForDebianTesting +# +# If your situation does not fit into one of the above situations, you should +# re-consider the override. If your override is incorrect, the build failure +# will crop up again later, in the crate that depends on this broken crate. diff --git a/tests/configs/http-types-2/debian/debcargo.toml b/tests/configs/http-types-2/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/http-types-2/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/http-types-2/debian/patches/fix-features.patch b/tests/configs/http-types-2/debian/patches/fix-features.patch new file mode 100644 index 0000000..c357f23 --- /dev/null +++ b/tests/configs/http-types-2/debian/patches/fix-features.patch @@ -0,0 +1,11 @@ +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -83,7 +83,7 @@ + version = "0.2.0" + + [features] +-async_std = ["fs"] ++async_std = ["async-std"] # replaces fs with deps of fs + cookie-secure = ["cookies", "cookie/secure"] + cookies = ["cookie"] + default = ["fs", "cookie-secure"] diff --git a/tests/configs/http-types-2/debian/patches/series b/tests/configs/http-types-2/debian/patches/series new file mode 100644 index 0000000..2447948 --- /dev/null +++ b/tests/configs/http-types-2/debian/patches/series @@ -0,0 +1 @@ +fix-features.patch diff --git a/tests/configs/hyperx-1/debian/debcargo.toml b/tests/configs/hyperx-1/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/hyperx-1/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/hyperx-1/debian/patches/relax-percent-encoding.patch b/tests/configs/hyperx-1/debian/patches/relax-percent-encoding.patch new file mode 100644 index 0000000..1868251 --- /dev/null +++ b/tests/configs/hyperx-1/debian/patches/relax-percent-encoding.patch @@ -0,0 +1,13 @@ +Index: hyperx-1.4.0/Cargo.toml +=================================================================== +--- hyperx-1.4.0.orig/Cargo.toml ++++ hyperx-1.4.0/Cargo.toml +@@ -43,7 +43,7 @@ version = ">=0.3.1, <0.4" + version = ">=0.3.2, <0.4" + + [dependencies.percent-encoding] +-version = ">=2.1.0, <2.2" ++version = "2.1.0" + + [dependencies.unicase] + version = ">=2.6.0, <2.7" diff --git a/tests/configs/hyperx-1/debian/patches/series b/tests/configs/hyperx-1/debian/patches/series new file mode 100644 index 0000000..0490e99 --- /dev/null +++ b/tests/configs/hyperx-1/debian/patches/series @@ -0,0 +1 @@ +relax-percent-encoding.patch diff --git a/tests/configs/hyperx/debian/debcargo.toml b/tests/configs/hyperx/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/hyperx/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/hyperx/debian/patches/relax-percent-encoding.patch b/tests/configs/hyperx/debian/patches/relax-percent-encoding.patch new file mode 100644 index 0000000..1868251 --- /dev/null +++ b/tests/configs/hyperx/debian/patches/relax-percent-encoding.patch @@ -0,0 +1,13 @@ +Index: hyperx-1.4.0/Cargo.toml +=================================================================== +--- hyperx-1.4.0.orig/Cargo.toml ++++ hyperx-1.4.0/Cargo.toml +@@ -43,7 +43,7 @@ version = ">=0.3.1, <0.4" + version = ">=0.3.2, <0.4" + + [dependencies.percent-encoding] +-version = ">=2.1.0, <2.2" ++version = "2.1.0" + + [dependencies.unicase] + version = ">=2.6.0, <2.7" diff --git a/tests/configs/hyperx/debian/patches/series b/tests/configs/hyperx/debian/patches/series new file mode 100644 index 0000000..0490e99 --- /dev/null +++ b/tests/configs/hyperx/debian/patches/series @@ -0,0 +1 @@ +relax-percent-encoding.patch diff --git a/tests/configs/jetscii-0.3/debian/debcargo.toml b/tests/configs/jetscii-0.3/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/jetscii-0.3/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/jetscii-0.3/debian/rules b/tests/configs/jetscii-0.3/debian/rules new file mode 100644 index 0000000..bfcc84f --- /dev/null +++ b/tests/configs/jetscii-0.3/debian/rules @@ -0,0 +1,28 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# code uses old rust feature that causes compiler error. +override_dh_auto_test: + dh_auto_test || true + +# This crate is actually broken; however there are several cases why this +# override is OK and necessary for Debian: +# +# 1. The other crate depends on this broken crate in an architecture-specific +# way i.e. with [target.$arch.*] in Cargo.toml +# +# cargo does not yet support omitting these dependencies on other arches [1] +# and so we are forced to include it also in debcargo [2] +# +# [1] https://github.com/rust-lang/cargo/issues/5896 +# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14 +# +# 2. The other crate depends on this broken crate only via one of its optional +# features that most users of that crate, don't need. You generally run into +# this situation when resolving via --resolve-type BinaryAllForDebianTesting +# +# If your situation does not fit into one of the above situations, you should +# re-consider the override. If your override is incorrect, the build failure +# will crop up again later, in the crate that depends on this broken crate. diff --git a/tests/configs/nasm-rs-0.2/debian/debcargo.toml b/tests/configs/nasm-rs-0.2/debian/debcargo.toml new file mode 100644 index 0000000..2c3ba3d --- /dev/null +++ b/tests/configs/nasm-rs-0.2/debian/debcargo.toml @@ -0,0 +1,4 @@ +semver_suffix = true + +[packages.lib] +depends = ["nasm"] diff --git a/tests/configs/nodrop-union-0.1/debian/debcargo.toml b/tests/configs/nodrop-union-0.1/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/nodrop-union-0.1/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/nodrop-union-0.1/debian/rules b/tests/configs/nodrop-union-0.1/debian/rules new file mode 100644 index 0000000..bfcc84f --- /dev/null +++ b/tests/configs/nodrop-union-0.1/debian/rules @@ -0,0 +1,28 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# code uses old rust feature that causes compiler error. +override_dh_auto_test: + dh_auto_test || true + +# This crate is actually broken; however there are several cases why this +# override is OK and necessary for Debian: +# +# 1. The other crate depends on this broken crate in an architecture-specific +# way i.e. with [target.$arch.*] in Cargo.toml +# +# cargo does not yet support omitting these dependencies on other arches [1] +# and so we are forced to include it also in debcargo [2] +# +# [1] https://github.com/rust-lang/cargo/issues/5896 +# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14 +# +# 2. The other crate depends on this broken crate only via one of its optional +# features that most users of that crate, don't need. You generally run into +# this situation when resolving via --resolve-type BinaryAllForDebianTesting +# +# If your situation does not fit into one of the above situations, you should +# re-consider the override. If your override is incorrect, the build failure +# will crop up again later, in the crate that depends on this broken crate. diff --git a/tests/configs/nom-4/debian/debcargo.toml b/tests/configs/nom-4/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/nom-4/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/nom-4/debian/rules b/tests/configs/nom-4/debian/rules new file mode 100644 index 0000000..bbc019c --- /dev/null +++ b/tests/configs/nom-4/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# crate declares benches outside of crate path +override_dh_auto_test: + dh_auto_test || true diff --git a/tests/configs/old-version/debian/debcargo.toml b/tests/configs/old-version/debian/debcargo.toml new file mode 100644 index 0000000..f205d5c --- /dev/null +++ b/tests/configs/old-version/debian/debcargo.toml @@ -0,0 +1 @@ +semver_suffix = true diff --git a/tests/configs/once-cell-1/debian/debcargo.toml b/tests/configs/once-cell-1/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/once-cell-1/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/once-cell-1/debian/patches/fix-feature.patch b/tests/configs/once-cell-1/debian/patches/fix-feature.patch new file mode 100644 index 0000000..68a43b9 --- /dev/null +++ b/tests/configs/once-cell-1/debian/patches/fix-feature.patch @@ -0,0 +1,22 @@ +Index: once_cell-1.16.0/Cargo.toml +=================================================================== +--- once_cell-1.16.0.orig/Cargo.toml ++++ once_cell-1.16.0/Cargo.toml +@@ -66,7 +66,7 @@ required-features = ["std"] + name = "test_synchronization" + required-features = ["std"] + +-[dependencies.atomic_polyfill] ++[dependencies.atomic_polyfill_renamed] + version = "1" + optional = true + package = "atomic-polyfill" +@@ -100,7 +100,7 @@ alloc = ["race"] + atomic-polyfill = ["critical-section"] + critical-section = [ + "critical_section", +- "atomic_polyfill", ++ "atomic_polyfill_renamed", + ] + default = ["std"] + parking_lot = ["parking_lot_core"] diff --git a/tests/configs/once-cell-1/debian/patches/series b/tests/configs/once-cell-1/debian/patches/series new file mode 100644 index 0000000..f4a83fe --- /dev/null +++ b/tests/configs/once-cell-1/debian/patches/series @@ -0,0 +1 @@ +fix-feature.patch diff --git a/tests/configs/only-macos/debian/debcargo.toml b/tests/configs/only-macos/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/only-macos/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/only-macos/debian/rules b/tests/configs/only-macos/debian/rules new file mode 100644 index 0000000..e3dbf82 --- /dev/null +++ b/tests/configs/only-macos/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# only supported on macos +override_dh_auto_test: + case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac diff --git a/tests/configs/only-windows/debian/debcargo.toml b/tests/configs/only-windows/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/only-windows/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/only-windows/debian/rules b/tests/configs/only-windows/debian/rules new file mode 100644 index 0000000..b92cbbc --- /dev/null +++ b/tests/configs/only-windows/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# only supported on windows +override_dh_auto_test: + case $(DEB_HOST_RUST_TYPE) in *-pc-windows-gnu) dh_auto_test;; *) true;; esac diff --git a/tests/configs/openssl-sys-0.9/debian/debcargo.toml b/tests/configs/openssl-sys-0.9/debian/debcargo.toml new file mode 100644 index 0000000..40666c0 --- /dev/null +++ b/tests/configs/openssl-sys-0.9/debian/debcargo.toml @@ -0,0 +1,4 @@ +semver_suffix = true + +[packages.lib] +depends = ["libssl-dev"] diff --git a/tests/configs/packed-simd-0.3/debian/debcargo.toml b/tests/configs/packed-simd-0.3/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/packed-simd-0.3/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/packed-simd-0.3/debian/rules b/tests/configs/packed-simd-0.3/debian/rules new file mode 100644 index 0000000..bfcc84f --- /dev/null +++ b/tests/configs/packed-simd-0.3/debian/rules @@ -0,0 +1,28 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# code uses old rust feature that causes compiler error. +override_dh_auto_test: + dh_auto_test || true + +# This crate is actually broken; however there are several cases why this +# override is OK and necessary for Debian: +# +# 1. The other crate depends on this broken crate in an architecture-specific +# way i.e. with [target.$arch.*] in Cargo.toml +# +# cargo does not yet support omitting these dependencies on other arches [1] +# and so we are forced to include it also in debcargo [2] +# +# [1] https://github.com/rust-lang/cargo/issues/5896 +# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14 +# +# 2. The other crate depends on this broken crate only via one of its optional +# features that most users of that crate, don't need. You generally run into +# this situation when resolving via --resolve-type BinaryAllForDebianTesting +# +# If your situation does not fit into one of the above situations, you should +# re-consider the override. If your override is incorrect, the build failure +# will crop up again later, in the crate that depends on this broken crate. diff --git a/tests/configs/petgraph-0.4/debian/debcargo.toml b/tests/configs/petgraph-0.4/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/petgraph-0.4/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/petgraph-0.4/debian/patches/fix-old-dep.patch b/tests/configs/petgraph-0.4/debian/patches/fix-old-dep.patch new file mode 100644 index 0000000..5ae18e6 --- /dev/null +++ b/tests/configs/petgraph-0.4/debian/patches/fix-old-dep.patch @@ -0,0 +1,40 @@ +Crate authors like to put dev-dependencies in dependencies due to +https://github.com/rust-lang/cargo/issues/1596 + +Then due to lack of maintenance this pollutes the dependency set with old +crates that no longer compile. + +Here, quickcheck 0.4 eventually depends on simd which no longer compiles. + +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -21,7 +21,7 @@ + license = "MIT/Apache-2.0" + repository = "https://github.com/bluss/petgraph" + [package.metadata.docs.rs] +-features = ["serde-1", "quickcheck"] ++features = ["serde-1"] + + [package.metadata.release] + no-dev-version = true +@@ -40,11 +40,6 @@ + version = "0.3.0" + optional = true + +-[dependencies.quickcheck] +-version = "0.4" +-optional = true +-default-features = false +- + [dependencies.serde] + version = "1.0" + optional = true +@@ -66,7 +61,7 @@ + version = "0.3" + + [features] +-all = ["unstable", "quickcheck", "stable_graph", "graphmap"] ++all = ["unstable", "stable_graph", "graphmap"] + default = ["graphmap", "stable_graph"] + generate = [] + graphmap = ["ordermap"] diff --git a/tests/configs/petgraph-0.4/debian/patches/series b/tests/configs/petgraph-0.4/debian/patches/series new file mode 100644 index 0000000..f385166 --- /dev/null +++ b/tests/configs/petgraph-0.4/debian/patches/series @@ -0,0 +1 @@ +fix-old-dep.patch diff --git a/tests/configs/phf-generator-0.10/debian/debcargo.toml b/tests/configs/phf-generator-0.10/debian/debcargo.toml new file mode 100644 index 0000000..7591b7f --- /dev/null +++ b/tests/configs/phf-generator-0.10/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +bin = false diff --git a/tests/configs/pkg-config-0.3/debian/debcargo.toml b/tests/configs/pkg-config-0.3/debian/debcargo.toml new file mode 100644 index 0000000..7a9da8f --- /dev/null +++ b/tests/configs/pkg-config-0.3/debian/debcargo.toml @@ -0,0 +1,4 @@ +semver_suffix = true + +[packages.lib] +depends = ["pkg-config"] diff --git a/tests/configs/redis-0.17/debian/debcargo.toml b/tests/configs/redis-0.17/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/redis-0.17/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/redis-0.17/debian/rules b/tests/configs/redis-0.17/debian/rules new file mode 100644 index 0000000..304a2b4 --- /dev/null +++ b/tests/configs/redis-0.17/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# crate is buggy; fails to declare "alloc" feature on futures-util +override_dh_auto_test: + dh_auto_test || true diff --git a/tests/configs/redox-syscall-0.1/debian/debcargo.toml b/tests/configs/redox-syscall-0.1/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/redox-syscall-0.1/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/redox-syscall-0.1/debian/rules b/tests/configs/redox-syscall-0.1/debian/rules new file mode 100644 index 0000000..bfcc84f --- /dev/null +++ b/tests/configs/redox-syscall-0.1/debian/rules @@ -0,0 +1,28 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# code uses old rust feature that causes compiler error. +override_dh_auto_test: + dh_auto_test || true + +# This crate is actually broken; however there are several cases why this +# override is OK and necessary for Debian: +# +# 1. The other crate depends on this broken crate in an architecture-specific +# way i.e. with [target.$arch.*] in Cargo.toml +# +# cargo does not yet support omitting these dependencies on other arches [1] +# and so we are forced to include it also in debcargo [2] +# +# [1] https://github.com/rust-lang/cargo/issues/5896 +# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14 +# +# 2. The other crate depends on this broken crate only via one of its optional +# features that most users of that crate, don't need. You generally run into +# this situation when resolving via --resolve-type BinaryAllForDebianTesting +# +# If your situation does not fit into one of the above situations, you should +# re-consider the override. If your override is incorrect, the build failure +# will crop up again later, in the crate that depends on this broken crate. diff --git a/tests/configs/redox-users-0.3/debian/debcargo.toml b/tests/configs/redox-users-0.3/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/redox-users-0.3/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/redox-users-0.3/debian/rules b/tests/configs/redox-users-0.3/debian/rules new file mode 100644 index 0000000..bfcc84f --- /dev/null +++ b/tests/configs/redox-users-0.3/debian/rules @@ -0,0 +1,28 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# code uses old rust feature that causes compiler error. +override_dh_auto_test: + dh_auto_test || true + +# This crate is actually broken; however there are several cases why this +# override is OK and necessary for Debian: +# +# 1. The other crate depends on this broken crate in an architecture-specific +# way i.e. with [target.$arch.*] in Cargo.toml +# +# cargo does not yet support omitting these dependencies on other arches [1] +# and so we are forced to include it also in debcargo [2] +# +# [1] https://github.com/rust-lang/cargo/issues/5896 +# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14 +# +# 2. The other crate depends on this broken crate only via one of its optional +# features that most users of that crate, don't need. You generally run into +# this situation when resolving via --resolve-type BinaryAllForDebianTesting +# +# If your situation does not fit into one of the above situations, you should +# re-consider the override. If your override is incorrect, the build failure +# will crop up again later, in the crate that depends on this broken crate. diff --git a/tests/configs/rhai-1/debian/debcargo.toml b/tests/configs/rhai-1/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/rhai-1/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/rhai-1/debian/rules b/tests/configs/rhai-1/debian/rules new file mode 100644 index 0000000..be9afde --- /dev/null +++ b/tests/configs/rhai-1/debian/rules @@ -0,0 +1,9 @@ +#!/usr/bin/make -f +%: + dh $@ --buildsystem cargo +export RUSTC_BOOTSTRAP := 1 +export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH) + +override_dh_dwz: + # Don't do anything. fails because of the + # https://github.com/rust-lang/rust/issues/66118 diff --git a/tests/configs/ring-0.13/debian/debcargo.toml b/tests/configs/ring-0.13/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/ring-0.13/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/ring-0.13/debian/rules b/tests/configs/ring-0.13/debian/rules new file mode 100644 index 0000000..f5e1085 --- /dev/null +++ b/tests/configs/ring-0.13/debian/rules @@ -0,0 +1,9 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# crate is super-over-zealous with lint settings +# overrides our cargo wrapper's --cap-lints warn +override_dh_auto_test: + dh_auto_test || true diff --git a/tests/configs/ring-0.14/debian/debcargo.toml b/tests/configs/ring-0.14/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/ring-0.14/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/ring-0.14/debian/rules b/tests/configs/ring-0.14/debian/rules new file mode 100644 index 0000000..f5e1085 --- /dev/null +++ b/tests/configs/ring-0.14/debian/rules @@ -0,0 +1,9 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# crate is super-over-zealous with lint settings +# overrides our cargo wrapper's --cap-lints warn +override_dh_auto_test: + dh_auto_test || true diff --git a/tests/configs/security-framework-2/debian/debcargo.toml b/tests/configs/security-framework-2/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/security-framework-2/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/security-framework-2/debian/rules b/tests/configs/security-framework-2/debian/rules new file mode 100644 index 0000000..e3dbf82 --- /dev/null +++ b/tests/configs/security-framework-2/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# only supported on macos +override_dh_auto_test: + case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac diff --git a/tests/configs/security-framework-sys-2/debian/debcargo.toml b/tests/configs/security-framework-sys-2/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/security-framework-sys-2/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/security-framework-sys-2/debian/rules b/tests/configs/security-framework-sys-2/debian/rules new file mode 100644 index 0000000..e3dbf82 --- /dev/null +++ b/tests/configs/security-framework-sys-2/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# only supported on macos +override_dh_auto_test: + case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac diff --git a/tests/configs/serde-qs-0.8/debian/debcargo.toml b/tests/configs/serde-qs-0.8/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/serde-qs-0.8/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/serde-qs-0.8/debian/patches/fix-actix-web-dep.patch b/tests/configs/serde-qs-0.8/debian/patches/fix-actix-web-dep.patch new file mode 100644 index 0000000..d44aff6 --- /dev/null +++ b/tests/configs/serde-qs-0.8/debian/patches/fix-actix-web-dep.patch @@ -0,0 +1,30 @@ +actix-web eventually depends on actix-connect 1.0.2 +actix-connect 1.0.2 depends on prerelease trust-dns-resolver 0.18.0-alpha.2, +which declares broken deps on trust-dns-proto "0.18.0" - should be "=0.18.0-alpha.2" + +debcargo doesn't support prerelease versions because semver compatibility +doesn't apply. So patching trust-dns-resolver to fix their bug is not an +option for us here. So just disable actix-web 2 here. + +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -30,12 +30,6 @@ + default-features = false + package = "actix-web" + +-[dependencies.actix-web2] +-version = "2.0" +-optional = true +-default-features = false +-package = "actix-web" +- + [dependencies.futures] + version = "0.3" + optional = true +@@ -75,6 +69,5 @@ + + [features] + actix = ["actix-web", "futures"] +-actix2 = ["actix-web2", "futures"] + default = [] + warp = ["futures", "tracing", "warp-framework"] diff --git a/tests/configs/serde-qs-0.8/debian/patches/series b/tests/configs/serde-qs-0.8/debian/patches/series new file mode 100644 index 0000000..03ed99c --- /dev/null +++ b/tests/configs/serde-qs-0.8/debian/patches/series @@ -0,0 +1 @@ +fix-actix-web-dep.patch diff --git a/tests/configs/servo-fontconfig-sys-5/debian/debcargo.toml b/tests/configs/servo-fontconfig-sys-5/debian/debcargo.toml new file mode 100644 index 0000000..83cc783 --- /dev/null +++ b/tests/configs/servo-fontconfig-sys-5/debian/debcargo.toml @@ -0,0 +1,4 @@ +semver_suffix = true + +[packages.lib] +depends = ["libfontconfig-dev"] diff --git a/tests/configs/sleef-sys-0.1/debian/debcargo.toml b/tests/configs/sleef-sys-0.1/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/sleef-sys-0.1/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/sleef-sys-0.1/debian/rules b/tests/configs/sleef-sys-0.1/debian/rules new file mode 100644 index 0000000..bfcc84f --- /dev/null +++ b/tests/configs/sleef-sys-0.1/debian/rules @@ -0,0 +1,28 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# code uses old rust feature that causes compiler error. +override_dh_auto_test: + dh_auto_test || true + +# This crate is actually broken; however there are several cases why this +# override is OK and necessary for Debian: +# +# 1. The other crate depends on this broken crate in an architecture-specific +# way i.e. with [target.$arch.*] in Cargo.toml +# +# cargo does not yet support omitting these dependencies on other arches [1] +# and so we are forced to include it also in debcargo [2] +# +# [1] https://github.com/rust-lang/cargo/issues/5896 +# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14 +# +# 2. The other crate depends on this broken crate only via one of its optional +# features that most users of that crate, don't need. You generally run into +# this situation when resolving via --resolve-type BinaryAllForDebianTesting +# +# If your situation does not fit into one of the above situations, you should +# re-consider the override. If your override is incorrect, the build failure +# will crop up again later, in the crate that depends on this broken crate. diff --git a/tests/configs/spin-0.4/debian/debcargo.toml b/tests/configs/spin-0.4/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/spin-0.4/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/spin-0.4/debian/rules b/tests/configs/spin-0.4/debian/rules new file mode 100644 index 0000000..bfcc84f --- /dev/null +++ b/tests/configs/spin-0.4/debian/rules @@ -0,0 +1,28 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# code uses old rust feature that causes compiler error. +override_dh_auto_test: + dh_auto_test || true + +# This crate is actually broken; however there are several cases why this +# override is OK and necessary for Debian: +# +# 1. The other crate depends on this broken crate in an architecture-specific +# way i.e. with [target.$arch.*] in Cargo.toml +# +# cargo does not yet support omitting these dependencies on other arches [1] +# and so we are forced to include it also in debcargo [2] +# +# [1] https://github.com/rust-lang/cargo/issues/5896 +# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14 +# +# 2. The other crate depends on this broken crate only via one of its optional +# features that most users of that crate, don't need. You generally run into +# this situation when resolving via --resolve-type BinaryAllForDebianTesting +# +# If your situation does not fit into one of the above situations, you should +# re-consider the override. If your override is incorrect, the build failure +# will crop up again later, in the crate that depends on this broken crate. diff --git a/tests/configs/tiny-keccak-2/debian/debcargo.toml b/tests/configs/tiny-keccak-2/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/tiny-keccak-2/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/tiny-keccak-2/debian/rules b/tests/configs/tiny-keccak-2/debian/rules new file mode 100644 index 0000000..bf5ebf1 --- /dev/null +++ b/tests/configs/tiny-keccak-2/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# crate errors on purpose when no features are enabled +override_dh_auto_test: + dh_auto_test || true diff --git a/tests/configs/vcpkg-0.2/debian/debcargo.toml b/tests/configs/vcpkg-0.2/debian/debcargo.toml new file mode 100644 index 0000000..0c74ebf --- /dev/null +++ b/tests/configs/vcpkg-0.2/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +excludes = ["test-data/**"] diff --git a/tests/configs/wepoll-ffi-0.1/debian/debcargo.toml b/tests/configs/wepoll-ffi-0.1/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/wepoll-ffi-0.1/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/wepoll-ffi-0.1/debian/rules b/tests/configs/wepoll-ffi-0.1/debian/rules new file mode 100644 index 0000000..b92cbbc --- /dev/null +++ b/tests/configs/wepoll-ffi-0.1/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# only supported on windows +override_dh_auto_test: + case $(DEB_HOST_RUST_TYPE) in *-pc-windows-gnu) dh_auto_test;; *) true;; esac diff --git a/tests/configs/windows-x86-64-gnullvm-0.42/debian/debcargo.toml b/tests/configs/windows-x86-64-gnullvm-0.42/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/windows-x86-64-gnullvm-0.42/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/windows-x86-64-gnullvm-0.42/debian/rules b/tests/configs/windows-x86-64-gnullvm-0.42/debian/rules new file mode 100755 index 0000000..6c28854 --- /dev/null +++ b/tests/configs/windows-x86-64-gnullvm-0.42/debian/rules @@ -0,0 +1,7 @@ +#!/usr/bin/make -f +%: + dh $@ --buildsystem cargo +export RUSTC_BOOTSTRAP := 1 +export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH) + +override_dh_strip: diff --git a/tests/configs/winreg-0.10/debian/debcargo.toml b/tests/configs/winreg-0.10/debian/debcargo.toml new file mode 100644 index 0000000..94ffe4a --- /dev/null +++ b/tests/configs/winreg-0.10/debian/debcargo.toml @@ -0,0 +1,2 @@ +semver_suffix = true +overlay = "." diff --git a/tests/configs/winreg-0.10/debian/rules b/tests/configs/winreg-0.10/debian/rules new file mode 100644 index 0000000..b92cbbc --- /dev/null +++ b/tests/configs/winreg-0.10/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +include /usr/share/rustc/architecture.mk +%: + dh $@ --buildsystem cargo + +# only supported on windows +override_dh_auto_test: + case $(DEB_HOST_RUST_TYPE) in *-pc-windows-gnu) dh_auto_test;; *) true;; esac diff --git a/tests/configs/zip-0.5/debian/debcargo.toml b/tests/configs/zip-0.5/debian/debcargo.toml new file mode 100644 index 0000000..66c76f8 --- /dev/null +++ b/tests/configs/zip-0.5/debian/debcargo.toml @@ -0,0 +1,3 @@ +semver_suffix = true +# tests purposefully contain invalid data that debian tools try to process +excludes = ["tests/**"] diff --git a/tests/debcargo_override.toml b/tests/debcargo_override.toml new file mode 100644 index 0000000..2141748 --- /dev/null +++ b/tests/debcargo_override.toml @@ -0,0 +1,14 @@ +uploaders = ["Sylvestre Ledru ", "Ximin Luo " ] + +[source] +section = "rust" +build_depends = ["libssl-dev"] +homepage = "https://salsa.debian.org/rust-team/debcargo-conf" + +[packages.bin] +summary = "Tool to create Debian package from Rust crate" +description = """ +This package provides debcargo a tool to create Debian source package from Rust +crate. The package created by this tool is as per the packaging policy set by +Debian Rust team. +""" diff --git a/tests/debcargo_override_top_level.toml b/tests/debcargo_override_top_level.toml new file mode 100644 index 0000000..13d0d95 --- /dev/null +++ b/tests/debcargo_override_top_level.toml @@ -0,0 +1,13 @@ +uploaders = ["Sylvestre Ledru ", "Ximin Luo " ] + +summary = "Tool to create Debian package from Rust crate" +description = """ +This package provides debcargo a tool to create Debian source package from Rust +crate. The package created by this tool is as per the packaging policy set by +Debian Rust team. +""" + +[source] +section = "rust" +build_depends = ["libssl-dev"] +homepage = "https://salsa.debian.org/rust-team/debcargo-conf" diff --git a/tests/sh/integrate.sh b/tests/sh/integrate.sh new file mode 100755 index 0000000..e2b542e --- /dev/null +++ b/tests/sh/integrate.sh @@ -0,0 +1,300 @@ +#!/bin/bash +set -e + +scriptdir="$(dirname "$0")" + +# outputs +directory=tmp +failures_file="" +# inputs +allow_failures="$scriptdir/build-allow-fail" +lintian_suppress_tags="$scriptdir/lintian-suppress-tags" +config_dir="$scriptdir/../configs" +# tweaks +run_lintian=true +run_sbuild=false +keepfiles=false +resolve= +extraargs= + +export DEBCARGO_TESTING_IGNORE_DEBIAN_POLICY_VIOLATION=1 +export DEBCARGO_TESTING_RUZT=1 + +export DEB_HOST_ARCH=${DEB_HOST_ARCH:-$(dpkg-architecture -qDEB_HOST_ARCH)} + +while getopts 'd:f:a:l:c:bkrRux:zh?' o; do + case $o in + d ) directory=$OPTARG;; + f ) failures_file=$OPTARG;; + + a ) allow_failures=$OPTARG;; + c ) config_dir=$OPTARG;; + + b ) run_sbuild=true;; + k ) keepfiles=true;; + r ) resolve=SourceForDebianUnstable;; + R ) resolve=BinaryAllForDebianTesting;; + x ) extraargs="$extraargs $OPTARG";; + h|\? ) cat >&2 <|) [..] + +Run debcargo, do a source-only build, and call lintian on the results. + + -h This help text. + +Options for output: + -d DIR Output directory, default: $directory. Warning: this will be + wiped at the start of the test! + -f FILE File to output failed crates in, instead of exiting non-zero. + Relative paths are taken relative to the output directory. + +Options for input: + -a FILE File that lists crate names to ignore failures for, default: + $allow_failures. + -c DIR Path to config directory, default: $config_dir. + +Options to control running: + -b Run sbuild on the resulting dsc package. + -k Don't wipe the output directory at the start of the test, and + don't rebuild a crate if its directory already exists. + -r Operate on all transitive build-dependencies of the source + package, needed for entry into Debian Unstable. + -R Operate on all transitive dependencies of the binary packages, + needed for entry into Debian Testing. + -x ARG Give ARG as an extra argument to debcargo, e.g. like + -x--copyright-guess-harder. +eof + exit 2;; + esac +done +shift $(expr $OPTIND - 1) + +allow_fail() { + local crate="$1" + local version="$2" + if ! test -f "${allow_failures}"; then + return 1 + elif grep -qx "${crate}" "${allow_failures}"; then + echo >&2 "Allowing ${crate} to fail..." + return 0 + elif [ -n "$version" ] && grep -qx "${crate}-${version}" "${allow_failures}"; then + echo >&2 "Allowing ${crate}-${version} to fail..." + return 0 + else + return 1 + fi +} + +shouldbuild() { + local dst="$1" + local src="$2" + test ! -e "$dst" -o "$src" -nt "$dst" +} + +changelog_pkgname() {( + local cratedir="$1" + cd "$cratedir" + # dpkg-parsechangelog is really slow when dealing with hundreds of crates + #echo $(dpkg-parsechangelog -SSource)_$(dpkg-parsechangelog -SVersion) + head -n1 debian/changelog | sed -nre 's/^(\S*) \((\S*)\).*/\1_\2/gp' +)} + +run_lintian() {( + local crate="$1" + local version="$2" + local cratedir="$crate${version:+-$version}" + cd "$directory" + + allow_fail "$crate" $version && return 0 + + local base="$(changelog_pkgname "$cratedir")" + local out="${base}.lintian.out" + + if ! ( shouldbuild "$out" "${base}_source.changes" \ + || shouldbuild "$out" "${base}_${DEB_HOST_ARCH}.changes" ); then + echo >&2 "skipping already-linted ${base}_*.changes in ${out}" + return 0 + fi + + echo >&2 "running lintian for ${base} into ${out}" + rm -f "$out" "${out}.tmp" + changes="${base}_source.changes" + lintian --suppress-tags-from-file "$lintian_suppress_tags" -EIL +pedantic "$changes" | tee -a "${out}.tmp" + changes="${base}_${DEB_HOST_ARCH}.changes" + lintian --suppress-tags-from-file "$lintian_suppress_tags" -EIL +pedantic "$changes" | tee -a "${out}.tmp" + mv "${out}.tmp" "$out" +)} + +if [ -z "$CHROOT" ]; then + if schroot -i -c "debcargo-unstable-${DEB_HOST_ARCH}-sbuild" >/dev/null 2>&1; then + CHROOT="debcargo-unstable-${DEB_HOST_ARCH}-sbuild" + else + CHROOT=${CHROOT:-unstable-"$DEB_HOST_ARCH"-sbuild} + fi +fi +GPG_KEY_ID="Debcargo Integration Test" +run_sbuild() {( + local crate="$1" + local version="$2" + local cratedir="$crate${version:+-$version}" + cd "$directory" + + allow_fail "$crate" $version && return 0 + local base="$(changelog_pkgname "$cratedir")" + local dsc="${base}.dsc" + local build="${base}_${DEB_HOST_ARCH}.build" + local changes="${base}_${DEB_HOST_ARCH}.changes" + + if ! shouldbuild "$changes" "$dsc"; then + echo >&2 "skipping already-built ${dsc} in ${changes}" + return 0 + fi + + if [ ! -f "signing-key.gpg" ]; then + mkdir -p "$PWD/gpg" + chmod 700 "$PWD/gpg" + GNUPGHOME="$PWD/gpg" gpg --batch --pinentry-mode=loopback --passphrase "" --quick-gen-key "$GPG_KEY_ID" + GNUPGHOME="$PWD/gpg" gpg --batch --export "$GPG_KEY_ID" > signing-key.gpg + fi + + # Update the local repo + apt-ftparchive packages . > Packages + apt-ftparchive release . > Release + GNUPGHOME="$PWD/gpg" gpg --batch -a --detach-sign -u "$GPG_KEY_ID" -o Release.gpg --yes Release + # We use --build-dep-resolver=aspcud as both apt/aptitude fail to resolve + # certain complex dependency situations e.g. bytes-0.4. For our official + # Debian rust packages we patch those crates to have simpler dependencies; + # but we don't want to maintain those patches for this integration test. + # We also pass criteria to minimise the Rust packages we take from the + # Debian archive, and maximise the ones generated by this test. + echo >&2 "sbuild $dsc logging to $build" + sbuild --arch-all --arch-any --no-run-lintian --build-dep-resolver=aspcud \ + --aspcud-criteria="-removed,-changed,-new,+count(solution,APT-Release:=/o=sbuild-build-depends-archive/),-count(solution,APT-Release:=/o=Debian/)" \ + --extra-repository="deb file:$(readlink -f "$directory") ./" --extra-repository-key="$PWD/signing-key.gpg" \ + -c "$CHROOT" -d unstable $SBUILD_EXTRA_ARGS "$dsc" +)} + +build_source() {( + local crate="$1" + local version="$2" + local cratedir="$crate${version:+-$version}" + cd "$directory" + + if [ -d "$cratedir" ]; then + if [ -f "$cratedir/debian/changelog" ]; then + local base="$(changelog_pkgname "$cratedir")" + if ! shouldbuild "${base}_source.buildinfo" "$cratedir/debian/changelog"; then + echo >&2 "skipping already-built ${cratedir}" + return 0 + fi + fi + rm -rf "$cratedir" + fi + + local deb_src_name="$($debcargo deb-src-name "$crate" "$version")" + local config="$config_dir/${deb_src_name}/debian/debcargo.toml" + if [ -f "$config" ]; then + option="--config $config" + if ! grep -q 'semver_suffix = true' "$config"; then + echo >&2 "bad config: $config must contain \"semver_suffix = true\"" + return 1 + fi + echo >&2 "using config: $config" + elif [ "$deb_src_name" != "$($debcargo deb-src-name "$crate" "")" ]; then + config="$config_dir/old-version/debian/debcargo.toml" + option="--config $config" + echo >&2 "using config: $config" + fi + + if ( set -x; $debcargo package $extraargs --no-overlay-write-back --directory $cratedir $option "${crate}" $version ); then + : + else + local x=$? + if allow_fail "$crate" $version; then + return 0 + fi + echo >&2 "crate failed: $crate $version" + if [ -n "$failures_file" ]; then + echo "$crate" $version >> "$failures_file" + return 0 + else + return $x + fi + fi + cd "${cratedir}" + mkdir -p debian/source + dpkg-buildpackage -d -S --no-sign +)} + +cargo_tree_rec() { + local resolve="$1" + shift + local cache="$directory/z.${*/\//_}.$resolve.list" + if [ ! -f "$cache" ]; then + "$debcargo" build-order --resolve-type "$resolve" \ + --config-dir "${config_dir}" "$@" > "$cache.tmp" + mv "$cache.tmp" "$cache" + fi + cat "$cache" +} + +run_x_or_deps() { + local x="$1" + shift + case "$x" in + *-[0-9]*) + spec="${x%-[0-9]*} ${x##*-}" + tree_args="${x%-[0-9]*}:${x##*-}" + ;; + *) + spec="$x" + tree_args="$x" + ;; + esac + if [ -n "$resolve" ]; then + set -o pipefail + cargo_tree_rec "$resolve" $tree_args | while read pkg ver extra; do + "$@" "$pkg" "${ver#v}" + done + set +o pipefail + fi + echo $spec | while read pkg ver extras; do + "$@" "$pkg" "${ver#v}" + done +} + +# make all paths absolute so things don't mess up when we switch dirs +allow_failures=$(readlink -f "$allow_failures") +lintian_suppress_tags=$(readlink -f "$lintian_suppress_tags") +config_dir=$(readlink -f "$config_dir") +directory=$(readlink -f "$directory") +scriptdir=$(readlink -f "$scriptdir") + +# ensure $directory exists and maybe wipe it +if ! $keepfiles; then + # don't rm the directory itself, in case it's a symlink + rm -rf "$directory"/* +fi +mkdir -p "$directory" + +cargo build +debcargo="$scriptdir/../../target/debug/debcargo" +test -x $debcargo + +for i in "$@"; do run_x_or_deps "$i" true; done +for i in "$@"; do run_x_or_deps "$i" build_source; done +# sudo schroot -c source:debcargo-unstable-amd64-sbuild -- sh -c 'echo "deb [allow-insecure=yes] file:/home/infinity0/var/lib/rust/debcargo-tmp ./" > /etc/apt/sources.list.d/local-debcargo-integration-test.list' +if $run_sbuild; then + if ! schroot -i -c "$CHROOT" >/dev/null; then + echo >&2 "create the $CHROOT schroot by running e.g.:" + echo >&2 " sudo sbuild-createchroot unstable /srv/chroot/$CHROOT http://deb.debian.org/debian" + echo >&2 " sudo schroot -c source:$CHROOT -- apt-get -y install dh-cargo" + echo >&2 " sudo sbuild-update -udr $CHROOT" + echo >&2 "See https://wiki.debian.org/sbuild for more details" + exit 1 + fi + for i in "$@"; do run_x_or_deps "$i" run_sbuild; done +fi +if $run_lintian; then + for i in "$@"; do run_x_or_deps "$i" run_lintian; done +fi diff --git a/tests/sh/lintian-suppress-tags b/tests/sh/lintian-suppress-tags new file mode 100644 index 0000000..003aa08 --- /dev/null +++ b/tests/sh/lintian-suppress-tags @@ -0,0 +1,88 @@ +## To be fixed by the package developer + +# copyright +missing-license-paragraph-in-dep5-copyright +space-in-std-shortname-in-dep5-copyright +license-problem-undefined-license +superfluous-file-pattern +bad-exception-format-in-dep5-copyright + +# other source +file-contains-fixme-placeholder +synopsis-too-long +description-synopsis-starts-with-article +debian-changelog-line-too-long +unknown-section +spelling-error-in-binary +rust-boilerplate +no-manual-page + +## To be fixed here in debcargo + +out-of-date-standards-version +package-uses-old-debhelper-compat-version +uses-debhelper-compat-file +upstream-metadata-file-is-missing + +## Cannot be fixed due to Rust crate conventions + +# crates don't generally have GPG signatures +debian-watch-does-not-check-gpg-signature +# crates don't always have a homepage +no-homepage-field +# crates don't always have tests +missing-tests-control +# crates sometimes just have long source lines +very-long-line-length-in-source-file + +## Not to be fixed, due to how we package Rust in Debian and lintian being presumptuous + +# we install the crate into /usr/share/cargo, including most docs +package-contains-documentation-outside-usr-share-doc +# we install the crate into /usr/share/cargo, lintian is overzealous +repeated-path-segment + +# this is a result of how we have to do rust packaging - +# see the comment relating to Multi-Arch in the debcargo source code +package-contains-no-arch-dependent-files + +# Provides issue with FTP pending resolution +field-too-long + +# bug 833608 +version-substvar-for-external-package + +# our policy is not to file ITPs for library crates since there are so many +initial-upload-closes-no-bugs + +## Not to be fixed, due to our test setup + +unused-override +# lintian doesn't recognise our test "ruzt" prefix +wrong-section-according-to-package-name + +# we don't finalise changelogs for this integration test +bad-distribution-in-changes-file +distribution-and-changes-mismatch + +# this is because we don't bother with excluding embedded libs in this +# integration test. they are not allowed in Debian and would be a FTP reject +# but we don't care for the purposes of this integration test + +source-is-missing +unpack-message-for-orig +unpack-message-for-deb-data +unpack-message-for-source +arch-dependent-file-in-usr-share +arch-dependent-file-not-in-arch-specific-directory +arch-dep-package-has-big-usr-share +embedded-library +font-outside-font-dir +font-in-non-font-package +duplicate-font-file +source-contains-autogenerated-gperf-data +source-contains-prebuilt-javascript-object +windows-devel-file-in-package +executable-not-elf-or-script +incorrect-path-for-interpreter +hardening-no-fortify-functions diff --git a/tests/sh/quick-build.sh b/tests/sh/quick-build.sh new file mode 100755 index 0000000..749716a --- /dev/null +++ b/tests/sh/quick-build.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -e +cargo build +rm -rf rust-* +target/debug/debcargo package "$@" +nano rust-${1/_/-}-*/debian/${file:-control}